Compare commits
2 Commits
cloud/v1.3
...
bl-teenage
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18d26737bb | ||
|
|
0d81c8b518 |
@@ -110,18 +110,16 @@ type KeysOfType<T, Match> = {
|
||||
}[keyof T]
|
||||
|
||||
class ConfirmDialog {
|
||||
private readonly root: Locator
|
||||
public readonly delete: Locator
|
||||
public readonly overwrite: Locator
|
||||
public readonly reject: Locator
|
||||
public readonly confirm: Locator
|
||||
|
||||
constructor(public readonly page: Page) {
|
||||
this.root = page.getByRole('dialog')
|
||||
this.delete = this.root.getByRole('button', { name: 'Delete' })
|
||||
this.overwrite = this.root.getByRole('button', { name: 'Overwrite' })
|
||||
this.reject = this.root.getByRole('button', { name: 'Cancel' })
|
||||
this.confirm = this.root.getByRole('button', { name: 'Confirm' })
|
||||
this.delete = page.locator('button.p-button[aria-label="Delete"]')
|
||||
this.overwrite = page.locator('button.p-button[aria-label="Overwrite"]')
|
||||
this.reject = page.locator('button.p-button[aria-label="Cancel"]')
|
||||
this.confirm = page.locator('button.p-button[aria-label="Confirm"]')
|
||||
}
|
||||
|
||||
async click(locator: KeysOfType<ConfirmDialog, Locator>) {
|
||||
|
||||
@@ -30,7 +30,7 @@ export class ComfyNodeSearchFilterSelectionPanel {
|
||||
async addFilter(filterValue: string, filterType: string) {
|
||||
await this.selectFilterType(filterType)
|
||||
await this.selectFilterValue(filterValue)
|
||||
await this.page.locator('button:has-text("Add")').click()
|
||||
await this.page.locator('.p-button-label:has-text("Add")').click()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +85,11 @@ test.describe('Missing models warning', () => {
|
||||
const missingModelsWarning = comfyPage.page.locator('.comfy-missing-models')
|
||||
await expect(missingModelsWarning).toBeVisible()
|
||||
|
||||
const downloadButton = missingModelsWarning.getByText('Download')
|
||||
const downloadButton = missingModelsWarning.getByLabel('Download')
|
||||
await expect(downloadButton).toBeVisible()
|
||||
|
||||
// Check that the copy URL button is also visible for Desktop environment
|
||||
const copyUrlButton = missingModelsWarning.getByText('Copy URL')
|
||||
const copyUrlButton = missingModelsWarning.getByLabel('Copy URL')
|
||||
await expect(copyUrlButton).toBeVisible()
|
||||
})
|
||||
|
||||
@@ -102,11 +102,11 @@ test.describe('Missing models warning', () => {
|
||||
const missingModelsWarning = comfyPage.page.locator('.comfy-missing-models')
|
||||
await expect(missingModelsWarning).toBeVisible()
|
||||
|
||||
const downloadButton = missingModelsWarning.getByText('Download')
|
||||
const downloadButton = missingModelsWarning.getByLabel('Download')
|
||||
await expect(downloadButton).toBeVisible()
|
||||
|
||||
// Check that the copy URL button is also visible for Desktop environment
|
||||
const copyUrlButton = missingModelsWarning.getByText('Copy URL')
|
||||
const copyUrlButton = missingModelsWarning.getByLabel('Copy URL')
|
||||
await expect(copyUrlButton).toBeVisible()
|
||||
})
|
||||
|
||||
@@ -176,7 +176,7 @@ test.describe('Missing models warning', () => {
|
||||
const missingModelsWarning = comfyPage.page.locator('.comfy-missing-models')
|
||||
await expect(missingModelsWarning).toBeVisible()
|
||||
|
||||
const downloadButton = comfyPage.page.getByText('Download')
|
||||
const downloadButton = comfyPage.page.getByLabel('Download')
|
||||
await expect(downloadButton).toBeVisible()
|
||||
const downloadPromise = comfyPage.page.waitForEvent('download')
|
||||
await downloadButton.click()
|
||||
@@ -290,7 +290,7 @@ test.describe('Settings', () => {
|
||||
// Save keybinding
|
||||
const saveButton = comfyPage.page
|
||||
.getByLabel('New Blank Workflow')
|
||||
.getByText('Save')
|
||||
.getByLabel('Save')
|
||||
await saveButton.click()
|
||||
|
||||
const request = await requestPromise
|
||||
|
||||
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
@@ -123,7 +123,8 @@ test.describe('Node Help', () => {
|
||||
await expect(helpPage).toContainText('KSampler')
|
||||
|
||||
// Click the back button - use a more specific selector
|
||||
const backButton = helpPage.getByRole('button', { name: /back/i })
|
||||
const backButton = comfyPage.page.locator('button:has(.pi-arrow-left)')
|
||||
await expect(backButton).toBeVisible()
|
||||
await backButton.click()
|
||||
|
||||
// Verify that we're back to the node library view
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 100 KiB |
@@ -100,7 +100,7 @@ test.describe('Node library sidebar', () => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
|
||||
await tab.getFolder('foo').click({ button: 'right' })
|
||||
await comfyPage.page.getByRole('menuitem', { name: 'New Folder' }).click()
|
||||
await comfyPage.page.getByLabel('New Folder').click()
|
||||
const textInput = comfyPage.page.locator('.editable-text input')
|
||||
await textInput.waitFor({ state: 'visible' })
|
||||
await textInput.fill('bar')
|
||||
@@ -203,7 +203,7 @@ test.describe('Node library sidebar', () => {
|
||||
await comfyPage.page
|
||||
.locator('.color-field .p-selectbutton > *:nth-child(2)')
|
||||
.click()
|
||||
await comfyPage.page.getByRole('button', { name: 'Confirm' }).click()
|
||||
await comfyPage.page.getByLabel('Confirm').click()
|
||||
await comfyPage.nextFrame()
|
||||
expect(
|
||||
await comfyPage.getSetting('Comfy.NodeLibrary.BookmarksCustomization')
|
||||
@@ -223,7 +223,7 @@ test.describe('Node library sidebar', () => {
|
||||
await comfyPage.page
|
||||
.locator('.icon-field .p-selectbutton > *:nth-child(2)')
|
||||
.click()
|
||||
await comfyPage.page.getByRole('button', { name: 'Confirm' }).click()
|
||||
await comfyPage.page.getByLabel('Confirm').click()
|
||||
await comfyPage.nextFrame()
|
||||
expect(
|
||||
await comfyPage.getSetting('Comfy.NodeLibrary.BookmarksCustomization')
|
||||
@@ -261,7 +261,7 @@ test.describe('Node library sidebar', () => {
|
||||
await comfyPage.page
|
||||
.locator('.icon-field .p-selectbutton > *:nth-child(2)')
|
||||
.click()
|
||||
await comfyPage.page.getByRole('button', { name: 'Confirm' }).click()
|
||||
await comfyPage.page.getByLabel('Confirm').click()
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Verify the color selection is saved
|
||||
|
||||
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 89 KiB |
43
initial-report.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Parallel Workflow Execution - Initial Findings
|
||||
|
||||
## Cloud PR 1743 behavior
|
||||
|
||||
- The PR enables parallel execution by making the per-user concurrency limit
|
||||
dynamic: `DispatchConfig.MaxConcurrentJobsPerUser` is added and read at runtime.
|
||||
- Dispatcher passes the runtime value into the state machine via
|
||||
`TriggerEventGloballyWithArgs`, so changes apply without restart.
|
||||
- State machine uses the runtime limit to move jobs from `queued_limited` to
|
||||
`queued_waiting`, which is the gate for concurrent execution.
|
||||
- Environment defaults set local/ephemeral/dev to 5 and staging/prod to 1.
|
||||
|
||||
Key references:
|
||||
- `common/dynamicconfig/types.go`
|
||||
- `services/dispatcher/server/services/job_dispatch/dispatcher.go`
|
||||
- `common/jobstate/state.go`
|
||||
- `common/dynamicconfig/defaults/ephemeral.json`
|
||||
- `infrastructure/dynamicconfig/prod/config.json`
|
||||
- `infrastructure/dynamicconfig/staging/config.json`
|
||||
- `infrastructure/dynamicconfig/dev/config.json`
|
||||
|
||||
## Frontend audit notes (current behavior)
|
||||
|
||||
- Queue fetching already supports multiple running/pending jobs via `/jobs`,
|
||||
mapping to `Running`/`Pending` lists.
|
||||
- `src/platform/remote/comfyui/jobs/fetchJobs.ts`
|
||||
- `src/stores/queueStore.ts`
|
||||
- The concurrency indicator is based on `runningWorkflowCount`, which counts
|
||||
prompts with running nodes and should reflect parallel execution.
|
||||
- `src/stores/executionStore.ts`
|
||||
- `src/components/queue/QueueProgressOverlay.vue`
|
||||
- The progress overlay is single-prompt focused:
|
||||
- `executionStore.activePromptId` tracks one prompt.
|
||||
- `nodeProgressStates` is overwritten per `progress_state`, so the progress
|
||||
bar and current node are effectively “last prompt wins.”
|
||||
- `src/stores/executionStore.ts`
|
||||
- `src/components/queue/QueueProgressOverlay.vue`
|
||||
|
||||
## Implication
|
||||
|
||||
If the frontend is expected to show per-job progress for multiple concurrent
|
||||
workflows, the execution store and progress overlay will need to evolve beyond
|
||||
the single-active-prompt assumption.
|
||||
107
plan.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Parallel Execution Implementation Plan
|
||||
|
||||
## Goals
|
||||
- Support multiple simultaneous workflow executions across:
|
||||
- Separate browser tabs (multiple clients).
|
||||
- In-app workflow tabs (multiple workflows).
|
||||
- Show correct progress, outputs, and errors per workflow/job.
|
||||
- Avoid regressions for single-run behavior and existing UI.
|
||||
|
||||
## Non-goals (initial scope)
|
||||
- Redesign the queue UI beyond enabling per-job progress indicators.
|
||||
- New backend features beyond prompt context for WS events (see dependencies).
|
||||
|
||||
## Dependencies / coordination (verified vs `../cloud-3` + `../backend-3`)
|
||||
- Prompt-scoped data is already available for core execution events:
|
||||
- `execution_start`, `execution_success`, `execution_error`, `execution_interrupted`,
|
||||
`executed`, `progress`, `progress_state`, and `executing` include `prompt_id` in
|
||||
`../backend-3` and are forwarded unchanged through `../cloud-3`.
|
||||
- Frontend currently *drops* `prompt_id` for `executing`; this can be fixed
|
||||
without backend changes.
|
||||
- `progress_text` is a binary message containing only `nodeId` + text in
|
||||
`../backend-3` (`send_progress_text`); `../cloud-3` forwards it as raw binary.
|
||||
- Per-prompt progress text is **not possible** without backend changes.
|
||||
- If we want it, we need to extend ComfyUI/backend to include `prompt_id` in
|
||||
the binary payload or add a parallel JSON event.
|
||||
- `b_preview_with_metadata` includes `prompt_id` and is forwarded as binary; usable
|
||||
as-is for prompt/workflow scoping.
|
||||
- Decide whether UI should filter by `client_id` (current tab only) or show all
|
||||
user jobs; document and apply consistently.
|
||||
|
||||
## Phase 1: Data model and execution state
|
||||
1. Refactor execution store to be prompt-centric:
|
||||
- Replace `activePromptId` single value with:
|
||||
- `promptStates: Record<promptId, PromptExecutionState>`.
|
||||
- `runningPromptIds` derived from prompt states.
|
||||
- `promptIdToWorkflowId` remains the primary routing map.
|
||||
- Provide selectors:
|
||||
- `getPromptProgress(promptId)`
|
||||
- `getPromptNodeProgressStates(promptId)`
|
||||
- `getWorkflowRunningPromptIds(workflowId)`
|
||||
- `getActivePromptForWorkflow(workflowId)` (if needed)
|
||||
- Redefine `isIdle` as `runningPromptIds.length === 0`.
|
||||
- Keep backward-compat getters but scope to:
|
||||
- active workflow tab, or
|
||||
- "most recently started prompt".
|
||||
|
||||
2. Update event handlers to write prompt-scoped state:
|
||||
- `execution_start` creates prompt state.
|
||||
- `progress_state` merges into `promptStates[promptId].nodeProgressStates`.
|
||||
- `execution_success/error/interrupted` remove prompt state only for that id.
|
||||
- Preserve initialization tracking per prompt.
|
||||
|
||||
## Phase 2: Prompt-aware node progress and canvas updates
|
||||
1. Update graph/node progress to be scoped to the active workflow tab:
|
||||
- Derive `nodeLocationProgressStates` from the prompt(s) mapped to the
|
||||
active workflow only.
|
||||
- Ensure `GraphCanvas.vue` applies progress for active workflow only.
|
||||
2. Update vue-node execution composables to use workflow-scoped progress.
|
||||
3. Update `groupNode` progress rendering to use prompt-scoped state.
|
||||
|
||||
## Phase 3: Outputs and previews isolation
|
||||
1. Introduce per-workflow output maps in `imagePreviewStore`:
|
||||
- `outputsByWorkflowId[workflowId][nodeLocatorId]`.
|
||||
- `previewsByWorkflowId[workflowId][nodeLocatorId]`.
|
||||
2. When switching active workflow tab:
|
||||
- Swap `app.nodeOutputs` and `app.nodePreviewImages` to the selected map.
|
||||
3. Update `executed` and `b_preview_with_metadata` handlers to:
|
||||
- Use `prompt_id -> workflowId` mapping to store outputs/previews in the
|
||||
correct workflow bucket.
|
||||
4. Update `ChangeTracker` and history loading paths to restore per-workflow
|
||||
outputs without overwriting other workflows.
|
||||
|
||||
## Phase 4: UI updates for multi-run visibility
|
||||
1. Queue overlay:
|
||||
- `useQueueProgress` to compute per-job progress for all running tasks.
|
||||
- `useJobList` to attach progress to each running item, not just one.
|
||||
- `QueueOverlayActive` to show aggregated or multi-job state.
|
||||
2. Actionbar interrupt:
|
||||
- Route interrupt to prompt(s) for the active workflow tab.
|
||||
- Optionally add a "stop all running" action.
|
||||
3. Browser tab title + favicon:
|
||||
- Use aggregate progress (e.g., max or average of running jobs) or show
|
||||
count of running jobs with a generic progress indicator.
|
||||
|
||||
## Phase 5: Browser-tab concurrency policy
|
||||
1. Decide and implement filtering:
|
||||
- Option A: show only jobs for the current `client_id` in the UI.
|
||||
- Option B: show all jobs for the user, but mark which client started them.
|
||||
2. Apply consistent filtering in:
|
||||
- queue overlay,
|
||||
- completion summary,
|
||||
- progress favicon/title.
|
||||
|
||||
## Phase 6: Tests and validation
|
||||
- Unit tests:
|
||||
- `executionStore` selectors and lifecycle per prompt.
|
||||
- `useQueueProgress` and `useJobList` showing per-job progress.
|
||||
- Update existing stories/tests that assume single `activePromptId`.
|
||||
- Manual validation checklist:
|
||||
- Two workflows running in two in-app tabs: progress and outputs isolated.
|
||||
- Two browser tabs running distinct workflows: no cross-talk in UI.
|
||||
- Interrupt from actionbar affects intended prompt(s).
|
||||
|
||||
## Phase 7: Rollout and cleanup
|
||||
- Remove deprecated single-prompt fields after migration is stable.
|
||||
- Update documentation/comments where prompt-scoped behavior is required.
|
||||
- Coordinate backend/WS schema changes with `../backend-3` and `../cloud-3`.
|
||||
@@ -5,23 +5,23 @@
|
||||
>
|
||||
<Button
|
||||
v-tooltip="{ value: $t('menu.showMenu'), showDelay: 300 }"
|
||||
variant="muted-textonly"
|
||||
size="lg"
|
||||
icon="pi pi-bars"
|
||||
severity="secondary"
|
||||
text
|
||||
size="large"
|
||||
:aria-label="$t('menu.showMenu')"
|
||||
aria-live="assertive"
|
||||
@click="exitFocusMode"
|
||||
@contextmenu="showNativeSystemMenu"
|
||||
>
|
||||
<i class="pi pi-bars" />
|
||||
</Button>
|
||||
/>
|
||||
<div class="window-actions-spacer" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { watchEffect } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||
|
||||
@@ -22,13 +22,12 @@
|
||||
value: item.tooltip,
|
||||
showDelay: 600
|
||||
}"
|
||||
:variant="item.key === queueMode ? 'primary' : 'secondary'"
|
||||
size="sm"
|
||||
class="w-full justify-start"
|
||||
>
|
||||
<i v-if="item.icon" :class="item.icon" />
|
||||
{{ String(item.label ?? '') }}
|
||||
</Button>
|
||||
:label="String(item.label ?? '')"
|
||||
:icon="item.icon"
|
||||
:severity="item.key === queueMode ? 'primary' : 'secondary'"
|
||||
size="small"
|
||||
text
|
||||
/>
|
||||
</template>
|
||||
</SplitButton>
|
||||
<BatchCountEdit />
|
||||
@@ -37,12 +36,12 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia'
|
||||
import Button from 'primevue/button'
|
||||
import type { MenuItem } from 'primevue/menuitem'
|
||||
import SplitButton from 'primevue/splitbutton'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { app } from '@/scripts/app'
|
||||
|
||||
@@ -46,22 +46,21 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
v-if="isShortcutsTabActive"
|
||||
variant="muted-textonly"
|
||||
size="sm"
|
||||
:label="$t('shortcuts.manageShortcuts')"
|
||||
icon="pi pi-cog"
|
||||
severity="secondary"
|
||||
size="small"
|
||||
text
|
||||
@click="openKeybindingSettings"
|
||||
>
|
||||
<i class="pi pi-cog" />
|
||||
{{ $t('shortcuts.manageShortcuts') }}
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
class="justify-self-end"
|
||||
variant="muted-textonly"
|
||||
size="sm"
|
||||
:aria-label="t('g.close')"
|
||||
icon="pi pi-times"
|
||||
severity="secondary"
|
||||
size="small"
|
||||
text
|
||||
@click="closeBottomPanel"
|
||||
>
|
||||
<i class="pi pi-times" />
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabList>
|
||||
@@ -80,6 +79,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Tab from 'primevue/tab'
|
||||
import type { TabPassThroughMethodOptions } from 'primevue/tab'
|
||||
import TabList from 'primevue/tablist'
|
||||
@@ -88,7 +88,6 @@ import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import ExtensionSlot from '@/components/common/ExtensionSlot.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore'
|
||||
import type { BottomPanelExtension } from '@/types/extensionTypes'
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
value: tooltipText,
|
||||
showDelay: 300
|
||||
}"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="pi pi-copy"
|
||||
severity="secondary"
|
||||
size="small"
|
||||
:class="
|
||||
cn('absolute top-2 right-8 transition-opacity', {
|
||||
'opacity-0 pointer-events-none select-none': !isHovered
|
||||
@@ -20,20 +21,18 @@
|
||||
"
|
||||
:aria-label="tooltipText"
|
||||
@click="handleCopy"
|
||||
>
|
||||
<i class="pi pi-copy" />
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useElementHover, useEventListener } from '@vueuse/core'
|
||||
import type { IDisposable } from '@xterm/xterm'
|
||||
import Button from 'primevue/button'
|
||||
import type { Ref } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useTerminal } from '@/composables/bottomPanelTabs/useTerminal'
|
||||
import { electronAPI, isElectron } from '@/utils/envUtil'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
@@ -7,24 +7,20 @@
|
||||
/>
|
||||
<Button
|
||||
v-tooltip="$t('g.upload')"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
:aria-label="$t('g.upload')"
|
||||
:icon="isUploading ? 'pi pi-spin pi-spinner' : 'pi pi-upload'"
|
||||
size="small"
|
||||
:disabled="isUploading"
|
||||
@click="triggerFileInput"
|
||||
>
|
||||
<i :class="isUploading ? 'pi pi-spin pi-spinner' : 'pi pi-upload'" />
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
v-tooltip="$t('g.clear')"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
:aria-label="$t('g.clear')"
|
||||
outlined
|
||||
icon="pi pi-trash"
|
||||
severity="danger"
|
||||
size="small"
|
||||
:disabled="!modelValue"
|
||||
@click="clearImage"
|
||||
>
|
||||
<i class="pi pi-trash" />
|
||||
</Button>
|
||||
/>
|
||||
<input
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
@@ -36,10 +32,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { api } from '@/scripts/api'
|
||||
|
||||
|
||||
@@ -27,19 +27,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button variant="textonly" @click="resetCustomization">
|
||||
<i class="pi pi-refresh" />
|
||||
{{ $t('g.reset') }}
|
||||
</Button>
|
||||
<Button autofocus @click="confirmCustomization">
|
||||
<i class="pi pi-check" />
|
||||
{{ $t('g.confirm') }}
|
||||
</Button>
|
||||
<Button
|
||||
:label="$t('g.reset')"
|
||||
icon="pi pi-refresh"
|
||||
class="p-button-text"
|
||||
@click="resetCustomization"
|
||||
/>
|
||||
<Button
|
||||
:label="$t('g.confirm')"
|
||||
icon="pi pi-check"
|
||||
autofocus
|
||||
@click="confirmCustomization"
|
||||
/>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Dialog from 'primevue/dialog'
|
||||
import Divider from 'primevue/divider'
|
||||
import SelectButton from 'primevue/selectbutton'
|
||||
@@ -47,7 +52,6 @@ import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import ColorCustomizationSelector from '@/components/common/ColorCustomizationSelector.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -16,22 +16,20 @@
|
||||
<Button
|
||||
v-if="status === null || status === 'error'"
|
||||
class="file-action-button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
:label="$t('g.download') + ' (' + fileSize + ')'"
|
||||
size="small"
|
||||
outlined
|
||||
:disabled="!!props.error"
|
||||
icon="pi pi-download"
|
||||
@click="triggerDownload"
|
||||
>
|
||||
<i class="pi pi-download" />
|
||||
{{ $t('g.download') + ' (' + fileSize + ')' }}
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
v-if="(status === null || status === 'error') && !!props.url"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
:label="$t('g.copyURL')"
|
||||
size="small"
|
||||
outlined
|
||||
@click="copyURL"
|
||||
>
|
||||
{{ $t('g.copyURL') }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -51,48 +49,44 @@
|
||||
v-if="status === 'in_progress'"
|
||||
v-tooltip.top="t('electronFileDownload.pause')"
|
||||
class="file-action-button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
size="small"
|
||||
outlined
|
||||
:disabled="!!props.error"
|
||||
icon="pi pi-pause-circle"
|
||||
@click="triggerPauseDownload"
|
||||
>
|
||||
<i class="pi pi-pause-circle" />
|
||||
</Button>
|
||||
/>
|
||||
|
||||
<Button
|
||||
v-if="status === 'paused'"
|
||||
v-tooltip.top="t('electronFileDownload.resume')"
|
||||
class="file-action-button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
:aria-label="t('electronFileDownload.resume')"
|
||||
size="small"
|
||||
outlined
|
||||
:disabled="!!props.error"
|
||||
icon="pi pi-play-circle"
|
||||
@click="triggerResumeDownload"
|
||||
>
|
||||
<i class="pi pi-play-circle" />
|
||||
</Button>
|
||||
/>
|
||||
|
||||
<Button
|
||||
v-tooltip.top="t('electronFileDownload.cancel')"
|
||||
class="file-action-button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
:aria-label="t('electronFileDownload.cancel')"
|
||||
size="small"
|
||||
outlined
|
||||
:disabled="!!props.error"
|
||||
icon="pi pi-times-circle"
|
||||
severity="danger"
|
||||
@click="triggerCancelDownload"
|
||||
>
|
||||
<i class="pi pi-times-circle" />
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import ProgressBar from 'primevue/progressbar'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||
import { useDownload } from '@/composables/useDownload'
|
||||
import { useElectronDownloadStore } from '@/stores/electronDownloadStore'
|
||||
|
||||
@@ -22,27 +22,31 @@
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
:label="$t('g.download') + ' (' + fileSize + ')'"
|
||||
size="small"
|
||||
outlined
|
||||
:disabled="!!props.error"
|
||||
:title="props.url"
|
||||
@click="download.triggerBrowserDownload"
|
||||
>
|
||||
{{ $t('g.download') + ' (' + fileSize + ')' }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="secondary" :disabled="!!props.error" @click="copyURL">
|
||||
{{ $t('g.copyURL') }}
|
||||
</Button>
|
||||
<Button
|
||||
:label="$t('g.copyURL')"
|
||||
size="small"
|
||||
outlined
|
||||
:disabled="!!props.error"
|
||||
@click="copyURL"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Message from 'primevue/message'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||
import { useDownload } from '@/composables/useDownload'
|
||||
import { formatSize } from '@/utils/formatUtil'
|
||||
|
||||
@@ -14,20 +14,21 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<Button size="sm" @click="triggerFileInput">
|
||||
<i class="pi pi-upload" />
|
||||
{{ $t('g.upload') }}
|
||||
</Button>
|
||||
<Button
|
||||
icon="pi pi-upload"
|
||||
:label="$t('g.upload')"
|
||||
size="small"
|
||||
@click="triggerFileInput"
|
||||
/>
|
||||
<Button
|
||||
v-if="modelValue"
|
||||
class="w-full"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
:aria-label="$t('g.delete')"
|
||||
outlined
|
||||
icon="pi pi-trash"
|
||||
severity="danger"
|
||||
size="small"
|
||||
@click="clearImage"
|
||||
>
|
||||
<i class="pi pi-trash" />
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
@@ -41,10 +42,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
}>()
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
</p>
|
||||
<Button
|
||||
v-if="buttonLabel"
|
||||
variant="textonly"
|
||||
:label="buttonLabel"
|
||||
class="p-button-text"
|
||||
@click="$emit('action')"
|
||||
>
|
||||
{{ buttonLabel }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
@@ -22,10 +21,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Card from 'primevue/card'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: string
|
||||
icon?: string
|
||||
|
||||
@@ -11,25 +11,24 @@
|
||||
<ApiNodesList :node-names="apiNodeNames" />
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<Button variant="textonly" @click="handleLearnMoreClick">
|
||||
{{ t('g.learnMore') }}
|
||||
</Button>
|
||||
<Button :label="t('g.learnMore')" link @click="handleLearnMoreClick" />
|
||||
<div class="flex gap-2">
|
||||
<Button variant="secondary" @click="onCancel?.()">
|
||||
{{ t('g.cancel') }}
|
||||
</Button>
|
||||
<Button @click="onLogin?.()">
|
||||
{{ t('g.login') }}
|
||||
</Button>
|
||||
<Button
|
||||
:label="t('g.cancel')"
|
||||
outlined
|
||||
severity="secondary"
|
||||
@click="onCancel?.()"
|
||||
/>
|
||||
<Button :label="t('g.login')" @click="onLogin?.()" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useExternalLink } from '@/composables/useExternalLink'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -31,64 +31,69 @@
|
||||
}}</label>
|
||||
</div>
|
||||
|
||||
<Button variant="secondary" autofocus @click="onCancel">
|
||||
<i class="pi pi-undo" />
|
||||
{{ $t('g.cancel') }}
|
||||
</Button>
|
||||
<Button v-if="type === 'default'" variant="primary" @click="onConfirm">
|
||||
<i class="pi pi-check" />
|
||||
{{ $t('g.confirm') }}
|
||||
</Button>
|
||||
<Button
|
||||
:label="$t('g.cancel')"
|
||||
icon="pi pi-undo"
|
||||
severity="secondary"
|
||||
autofocus
|
||||
@click="onCancel"
|
||||
/>
|
||||
<Button
|
||||
v-if="type === 'default'"
|
||||
:label="$t('g.confirm')"
|
||||
severity="primary"
|
||||
icon="pi pi-check"
|
||||
@click="onConfirm"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="type === 'delete'"
|
||||
variant="destructive"
|
||||
:label="$t('g.delete')"
|
||||
severity="danger"
|
||||
icon="pi pi-trash"
|
||||
@click="onConfirm"
|
||||
>
|
||||
<i class="pi pi-trash" />
|
||||
{{ $t('g.delete') }}
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
v-else-if="type === 'overwrite' || type === 'overwriteBlueprint'"
|
||||
variant="destructive"
|
||||
:label="$t('g.overwrite')"
|
||||
severity="warn"
|
||||
icon="pi pi-save"
|
||||
@click="onConfirm"
|
||||
>
|
||||
<i class="pi pi-save" />
|
||||
{{ $t('g.overwrite') }}
|
||||
</Button>
|
||||
/>
|
||||
<template v-else-if="type === 'dirtyClose'">
|
||||
<Button variant="secondary" @click="onDeny">
|
||||
<i class="pi pi-times" />
|
||||
{{ $t('g.no') }}
|
||||
</Button>
|
||||
<Button @click="onConfirm">
|
||||
<i class="pi pi-save" />
|
||||
{{ $t('g.save') }}
|
||||
</Button>
|
||||
<Button
|
||||
:label="$t('g.no')"
|
||||
severity="secondary"
|
||||
icon="pi pi-times"
|
||||
@click="onDeny"
|
||||
/>
|
||||
<Button :label="$t('g.save')" icon="pi pi-save" @click="onConfirm" />
|
||||
</template>
|
||||
<Button
|
||||
v-else-if="type === 'reinstall'"
|
||||
variant="destructive"
|
||||
:label="$t('desktopMenu.reinstall')"
|
||||
severity="warn"
|
||||
icon="pi pi-eraser"
|
||||
@click="onConfirm"
|
||||
>
|
||||
<i class="pi pi-eraser" />
|
||||
{{ $t('desktopMenu.reinstall') }}
|
||||
</Button>
|
||||
/>
|
||||
<!-- Invalid - just show a close button. -->
|
||||
<Button v-else variant="primary" @click="onCancel">
|
||||
<i class="pi pi-times" />
|
||||
{{ $t('g.close') }}
|
||||
</Button>
|
||||
<Button
|
||||
v-else
|
||||
:label="$t('g.close')"
|
||||
severity="primary"
|
||||
icon="pi pi-times"
|
||||
@click="onCancel"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Checkbox from 'primevue/checkbox'
|
||||
import Message from 'primevue/message'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import type { ConfirmationDialogType } from '@/services/dialogService'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
|
||||
@@ -14,16 +14,18 @@
|
||||
</template>
|
||||
|
||||
<div class="flex justify-center gap-2">
|
||||
<Button v-show="!reportOpen" variant="textonly" @click="showReport">
|
||||
{{ $t('g.showReport') }}
|
||||
</Button>
|
||||
<Button
|
||||
v-show="!reportOpen"
|
||||
variant="textonly"
|
||||
text
|
||||
:label="$t('g.showReport')"
|
||||
@click="showReport"
|
||||
/>
|
||||
<Button
|
||||
v-show="!reportOpen"
|
||||
text
|
||||
:label="$t('issueReport.helpFix')"
|
||||
@click="showContactSupport"
|
||||
>
|
||||
{{ $t('issueReport.helpFix') }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
<template v-if="reportOpen">
|
||||
<Divider />
|
||||
@@ -38,15 +40,18 @@
|
||||
:repo-owner="repoOwner"
|
||||
:repo-name="repoName"
|
||||
/>
|
||||
<Button v-if="reportOpen" @click="copyReportToClipboard">
|
||||
<i class="pi pi-copy" />
|
||||
{{ $t('g.copyToClipboard') }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="reportOpen"
|
||||
:label="$t('g.copyToClipboard')"
|
||||
icon="pi pi-copy"
|
||||
@click="copyReportToClipboard"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import ScrollPanel from 'primevue/scrollpanel'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
@@ -55,7 +60,6 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import FindIssueButton from '@/components/dialog/content/error/FindIssueButton.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { api } from '@/scripts/api'
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import FloatLabel from 'primevue/floatlabel'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -51,7 +51,8 @@
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
variant="secondary"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="signInWithGoogle"
|
||||
>
|
||||
<i class="pi pi-google mr-2"></i>
|
||||
@@ -65,7 +66,8 @@
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
variant="secondary"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="signInWithGithub"
|
||||
>
|
||||
<i class="pi pi-github mr-2"></i>
|
||||
@@ -80,7 +82,8 @@
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
variant="secondary"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="showApiKeyForm = true"
|
||||
>
|
||||
<img
|
||||
@@ -139,12 +142,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import Message from 'primevue/message'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { getComfyPlatformBaseUrl } from '@/config/comfyApi'
|
||||
import {
|
||||
|
||||
@@ -58,27 +58,26 @@
|
||||
<Button
|
||||
:disabled="!selectedCredits || loading"
|
||||
:loading="loading"
|
||||
variant="primary"
|
||||
:class="cn('w-full', (!selectedCredits || loading) && 'opacity-30')"
|
||||
severity="primary"
|
||||
:label="$t('credits.topUp.buy')"
|
||||
:class="['w-full', { 'opacity-30': !selectedCredits || loading }]"
|
||||
:pt="{ label: { class: 'text-primary-foreground' } }"
|
||||
@click="handleBuy"
|
||||
>
|
||||
{{ $t('credits.topUp.buy') }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { creditsToUsd } from '@/base/credits/comfyCredits'
|
||||
import UserCredit from '@/components/common/UserCredit.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import CreditTopUpOption from './credit/CreditTopUpOption.vue'
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
<PasswordFields />
|
||||
|
||||
<!-- Submit Button -->
|
||||
<Button type="submit" class="mt-4 h-10 font-medium" :loading="loading">
|
||||
{{ $t('userSettings.updatePassword') }}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
:label="$t('userSettings.updatePassword')"
|
||||
class="mt-4 h-10 font-medium"
|
||||
:loading="loading"
|
||||
/>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
@@ -17,10 +20,10 @@
|
||||
import type { FormSubmitEvent } from '@primevue/forms'
|
||||
import { Form } from '@primevue/forms'
|
||||
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
||||
import Button from 'primevue/button'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import PasswordFields from '@/components/dialog/content/signin/PasswordFields.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { updatePasswordSchema } from '@/schemas/signInSchema'
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<template>
|
||||
<Button variant="secondary" @click="openGitHubIssues">
|
||||
<i class="pi pi-github" />
|
||||
{{ $t('g.findIssues') }}
|
||||
</Button>
|
||||
<Button
|
||||
:label="$t('g.findIssues')"
|
||||
severity="secondary"
|
||||
icon="pi pi-github"
|
||||
@click="openGitHubIssues"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -10,23 +10,15 @@
|
||||
<div>
|
||||
{{ $t('g.currentUser') }}: {{ userStore.currentUser?.username }}
|
||||
</div>
|
||||
<Button
|
||||
class="text-inherit"
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
:aria-label="$t('menuLabels.Sign Out')"
|
||||
@click="logout"
|
||||
>
|
||||
<i class="pi pi-sign-out" />
|
||||
</Button>
|
||||
<Button icon="pi pi-sign-out" text @click="logout" />
|
||||
</div>
|
||||
</Message>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Message from 'primevue/message'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useUserStore } from '@/stores/userStore'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
@@ -23,33 +23,24 @@
|
||||
<template #body="slotProps">
|
||||
<div class="actions invisible flex flex-row">
|
||||
<Button
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
:aria-label="$t('g.edit')"
|
||||
icon="pi pi-pencil"
|
||||
class="p-button-text"
|
||||
@click="editKeybinding(slotProps.data)"
|
||||
>
|
||||
<i class="pi pi-pencil" />
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
:aria-label="$t('g.reset')"
|
||||
icon="pi pi-replay"
|
||||
class="p-button-text p-button-warn"
|
||||
:disabled="
|
||||
!keybindingStore.isCommandKeybindingModified(slotProps.data.id)
|
||||
"
|
||||
@click="resetKeybinding(slotProps.data)"
|
||||
>
|
||||
<i class="pi pi-replay" />
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
:aria-label="$t('g.delete')"
|
||||
icon="pi pi-trash"
|
||||
class="p-button-text p-button-danger"
|
||||
:disabled="!slotProps.data.keybinding"
|
||||
@click="removeKeybinding(slotProps.data)"
|
||||
>
|
||||
<i class="pi pi-trash" />
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
@@ -113,31 +104,30 @@
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button
|
||||
:variant="existingKeybindingOnCombo ? 'destructive' : 'primary'"
|
||||
:label="existingKeybindingOnCombo ? 'Overwrite' : 'Save'"
|
||||
:icon="existingKeybindingOnCombo ? 'pi pi-pencil' : 'pi pi-check'"
|
||||
:severity="existingKeybindingOnCombo ? 'warn' : undefined"
|
||||
autofocus
|
||||
@click="saveKeybinding"
|
||||
>
|
||||
<i
|
||||
:class="existingKeybindingOnCombo ? 'pi pi-pencil' : 'pi pi-check'"
|
||||
/>
|
||||
{{ existingKeybindingOnCombo ? $t('g.overwrite') : $t('g.save') }}
|
||||
</Button>
|
||||
/>
|
||||
</template>
|
||||
</Dialog>
|
||||
<Button
|
||||
v-tooltip="$t('g.resetAllKeybindingsTooltip')"
|
||||
class="mt-4 w-full"
|
||||
variant="destructive-textonly"
|
||||
class="mt-4"
|
||||
:label="$t('g.resetAll')"
|
||||
icon="pi pi-replay"
|
||||
severity="danger"
|
||||
fluid
|
||||
text
|
||||
@click="resetAllKeybindings"
|
||||
>
|
||||
<i class="pi pi-replay" />
|
||||
{{ $t('g.resetAll') }}
|
||||
</Button>
|
||||
/>
|
||||
</PanelTemplate>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FilterMatchMode } from '@primevue/core/api'
|
||||
import Button from 'primevue/button'
|
||||
import Column from 'primevue/column'
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Dialog from 'primevue/dialog'
|
||||
@@ -149,7 +139,6 @@ import { computed, ref, watchEffect } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import SearchBox from '@/components/common/SearchBox.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useKeybindingService } from '@/services/keybindingService'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import {
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
<Skeleton v-if="loading" width="2rem" height="2rem" />
|
||||
<Button
|
||||
v-else-if="isActiveSubscription"
|
||||
:label="$t('credits.purchaseCredits')"
|
||||
:loading="loading"
|
||||
@click="handlePurchaseCreditsClick"
|
||||
>
|
||||
{{ $t('credits.purchaseCredits') }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<Skeleton
|
||||
@@ -34,26 +33,25 @@
|
||||
{{ $t('credits.lastUpdated') }}: {{ formattedLastUpdateTime }}
|
||||
</div>
|
||||
<Button
|
||||
variant="muted-textonly"
|
||||
size="icon-sm"
|
||||
:aria-label="$t('g.refresh')"
|
||||
icon="pi pi-refresh"
|
||||
text
|
||||
size="small"
|
||||
severity="secondary"
|
||||
@click="() => authActions.fetchBalance()"
|
||||
>
|
||||
<i class="pi pi-refresh" />
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<h3>{{ $t('credits.activity') }}</h3>
|
||||
<Button
|
||||
variant="muted-textonly"
|
||||
:label="$t('credits.invoiceHistory')"
|
||||
text
|
||||
severity="secondary"
|
||||
icon="pi pi-arrow-up-right"
|
||||
:loading="loading"
|
||||
@click="handleCreditsHistoryClick"
|
||||
>
|
||||
<i class="pi pi-arrow-up-right" />
|
||||
{{ $t('credits.invoiceHistory') }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-if="creditHistory.length > 0">
|
||||
@@ -88,24 +86,34 @@
|
||||
<UsageLogsTable ref="usageLogsTableRef" />
|
||||
|
||||
<div class="flex flex-row gap-2">
|
||||
<Button variant="muted-textonly" @click="handleFaqClick">
|
||||
<i class="pi pi-question-circle" />
|
||||
{{ $t('credits.faqs') }}
|
||||
</Button>
|
||||
<Button variant="muted-textonly" @click="handleOpenPartnerNodesInfo">
|
||||
<i class="pi pi-question-circle" />
|
||||
{{ $t('subscription.partnerNodesCredits') }}
|
||||
</Button>
|
||||
<Button variant="muted-textonly" @click="handleMessageSupport">
|
||||
<i class="pi pi-comments" />
|
||||
{{ $t('credits.messageSupport') }}
|
||||
</Button>
|
||||
<Button
|
||||
:label="$t('credits.faqs')"
|
||||
text
|
||||
severity="secondary"
|
||||
icon="pi pi-question-circle"
|
||||
@click="handleFaqClick"
|
||||
/>
|
||||
<Button
|
||||
:label="$t('subscription.partnerNodesCredits')"
|
||||
text
|
||||
severity="secondary"
|
||||
icon="pi pi-question-circle"
|
||||
@click="handleOpenPartnerNodesInfo"
|
||||
/>
|
||||
<Button
|
||||
:label="$t('credits.messageSupport')"
|
||||
text
|
||||
severity="secondary"
|
||||
icon="pi pi-comments"
|
||||
@click="handleMessageSupport"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Column from 'primevue/column'
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Divider from 'primevue/divider'
|
||||
@@ -115,7 +123,6 @@ import { computed, ref, watch } from 'vue'
|
||||
|
||||
import UserCredit from '@/components/common/UserCredit.vue'
|
||||
import UsageLogsTable from '@/components/dialog/content/setting/UsageLogsTable.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useExternalLink } from '@/composables/useExternalLink'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Badge from 'primevue/badge'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
import Column from 'primevue/column'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import DataTable from 'primevue/datatable'
|
||||
|
||||
@@ -78,12 +78,9 @@
|
||||
}
|
||||
}
|
||||
}"
|
||||
variant="textonly"
|
||||
size="icon-sm"
|
||||
:aria-label="$t('credits.additionalInfo')"
|
||||
>
|
||||
<i class="pi pi-info-circle" />
|
||||
</Button>
|
||||
icon="pi pi-info-circle"
|
||||
class="p-button-text p-button-sm"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
@@ -92,13 +89,13 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import Badge from 'primevue/badge'
|
||||
import Button from 'primevue/button'
|
||||
import Column from 'primevue/column'
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Message from 'primevue/message'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import type { AuditLog } from '@/services/customerEventsService'
|
||||
import {
|
||||
|
||||
@@ -44,12 +44,11 @@
|
||||
value: $t('userSettings.updatePassword'),
|
||||
showDelay: 300
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
size="icon-sm"
|
||||
icon="pi pi-pen-to-square"
|
||||
severity="secondary"
|
||||
text
|
||||
@click="dialogService.showUpdatePasswordDialog()"
|
||||
>
|
||||
<i class="pi pi-pen-to-square" />
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,18 +58,21 @@
|
||||
style="--pc-spinner-color: #000"
|
||||
/>
|
||||
<div v-else class="mt-4 flex flex-col gap-2">
|
||||
<Button class="w-32" variant="secondary" @click="handleSignOut">
|
||||
<i class="pi pi-sign-out" />
|
||||
{{ $t('auth.signOut.signOut') }}
|
||||
</Button>
|
||||
<Button
|
||||
class="w-32"
|
||||
severity="secondary"
|
||||
:label="$t('auth.signOut.signOut')"
|
||||
icon="pi pi-sign-out"
|
||||
@click="handleSignOut"
|
||||
/>
|
||||
<Button
|
||||
v-if="!isApiKeyLogin"
|
||||
class="w-fit"
|
||||
variant="destructive-textonly"
|
||||
variant="text"
|
||||
severity="danger"
|
||||
:label="$t('auth.deleteAccount.deleteAccount')"
|
||||
@click="handleDeleteAccount"
|
||||
>
|
||||
{{ $t('auth.deleteAccount.deleteAccount') }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -82,25 +84,24 @@
|
||||
|
||||
<Button
|
||||
class="w-52"
|
||||
variant="primary"
|
||||
severity="primary"
|
||||
:loading="loading"
|
||||
:label="$t('auth.login.signInOrSignUp')"
|
||||
icon="pi pi-user"
|
||||
@click="handleSignIn"
|
||||
>
|
||||
<i class="pi pi-user" />
|
||||
{{ $t('auth.login.signInOrSignUp') }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import TabPanel from 'primevue/tabpanel'
|
||||
|
||||
import UserAvatar from '@/components/common/UserAvatar.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Form } from '@primevue/forms'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createPinia } from 'pinia'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Message from 'primevue/message'
|
||||
@@ -99,10 +99,9 @@ describe('ApiKeyForm', () => {
|
||||
)
|
||||
await wrapper.find('form').trigger('submit')
|
||||
|
||||
const buttons = wrapper.findAllComponents(Button)
|
||||
const submitButton = buttons.find(
|
||||
(btn) => btn.attributes('type') === 'submit'
|
||||
)
|
||||
const submitButton = wrapper
|
||||
.findAllComponents(Button)
|
||||
.find((btn) => btn.text() === 'Save')
|
||||
expect(submitButton?.props('loading')).toBe(true)
|
||||
})
|
||||
|
||||
|
||||
@@ -67,15 +67,10 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<Button type="button" variant="textonly" @click="$emit('back')">
|
||||
<Button type="button" link @click="$emit('back')">
|
||||
{{ t('g.back') }}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
>
|
||||
<Button type="submit" :loading="loading" :disabled="loading">
|
||||
{{ t('g.save') }}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -87,12 +82,12 @@
|
||||
import type { FormSubmitEvent } from '@primevue/forms'
|
||||
import { Form } from '@primevue/forms'
|
||||
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
||||
import Button from 'primevue/button'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Message from 'primevue/message'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { getComfyPlatformBaseUrl } from '@/config/comfyApi'
|
||||
import {
|
||||
configValueOrDefault,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Form } from '@primevue/forms'
|
||||
import type { VueWrapper } from '@vue/test-utils'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Password from 'primevue/password'
|
||||
|
||||
@@ -64,11 +64,10 @@
|
||||
<Button
|
||||
v-else
|
||||
type="submit"
|
||||
:label="t('auth.login.loginButton')"
|
||||
class="mt-4 h-10 font-medium"
|
||||
:disabled="!$form.valid"
|
||||
>
|
||||
{{ t('auth.login.loginButton') }}
|
||||
</Button>
|
||||
/>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
@@ -77,6 +76,7 @@ import type { FormSubmitEvent } from '@primevue/forms'
|
||||
import { Form } from '@primevue/forms'
|
||||
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
||||
import { useThrottleFn } from '@vueuse/core'
|
||||
import Button from 'primevue/button'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Password from 'primevue/password'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
@@ -84,7 +84,6 @@ import { useToast } from 'primevue/usetoast'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { signInSchema } from '@/schemas/signInSchema'
|
||||
import type { SignInData } from '@/schemas/signInSchema'
|
||||
|
||||
@@ -33,11 +33,10 @@
|
||||
<Button
|
||||
v-else
|
||||
type="submit"
|
||||
:label="t('auth.signup.signUpButton')"
|
||||
class="mt-4 h-10 font-medium"
|
||||
:disabled="!$form.valid"
|
||||
>
|
||||
{{ t('auth.signup.signUpButton') }}
|
||||
</Button>
|
||||
/>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
@@ -46,12 +45,12 @@ import type { FormSubmitEvent } from '@primevue/forms'
|
||||
import { Form, FormField } from '@primevue/forms'
|
||||
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
||||
import { useThrottleFn } from '@vueuse/core'
|
||||
import Button from 'primevue/button'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { signUpSchema } from '@/schemas/signInSchema'
|
||||
import type { SignUpData } from '@/schemas/signInSchema'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
<template>
|
||||
<Button
|
||||
ref="buttonRef"
|
||||
variant="secondary"
|
||||
severity="secondary"
|
||||
class="group h-8 rounded-none! bg-comfy-menu-bg p-0 transition-none! hover:rounded-lg! hover:bg-interface-button-hover-surface!"
|
||||
:style="buttonStyles"
|
||||
@click="toggle"
|
||||
>
|
||||
<div class="flex items-center gap-1 pr-0.5">
|
||||
<div
|
||||
class="rounded-lg bg-interface-panel-selected-surface p-2 group-hover:bg-interface-button-hover-surface"
|
||||
>
|
||||
<i :class="currentModeIcon" class="block h-4 w-4" />
|
||||
<template #default>
|
||||
<div class="flex items-center gap-1 pr-0.5">
|
||||
<div
|
||||
class="rounded-lg bg-interface-panel-selected-surface p-2 group-hover:bg-interface-button-hover-surface"
|
||||
>
|
||||
<i :class="currentModeIcon" class="block h-4 w-4" />
|
||||
</div>
|
||||
<i class="icon-[lucide--chevron-down] block h-4 w-4 pr-1.5" />
|
||||
</div>
|
||||
<i class="icon-[lucide--chevron-down] block h-4 w-4 pr-1.5" />
|
||||
</div>
|
||||
</template>
|
||||
</Button>
|
||||
|
||||
<Popover
|
||||
@@ -54,10 +56,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Popover from 'primevue/popover'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
|
||||
@@ -24,18 +24,22 @@
|
||||
|
||||
<Button
|
||||
v-tooltip.top="fitViewTooltip"
|
||||
variant="secondary"
|
||||
severity="secondary"
|
||||
icon="pi pi-expand"
|
||||
:aria-label="fitViewTooltip"
|
||||
:style="stringifiedMinimapStyles.buttonStyles"
|
||||
class="h-8 w-8 bg-comfy-menu-bg p-0 hover:bg-interface-button-hover-surface!"
|
||||
@click="() => commandStore.execute('Comfy.Canvas.FitView')"
|
||||
>
|
||||
<i class="icon-[lucide--focus] h-4 w-4" />
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--focus] h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
v-tooltip.top="t('zoomControls.label')"
|
||||
variant="secondary"
|
||||
severity="secondary"
|
||||
:label="t('zoomControls.label')"
|
||||
:class="zoomButtonClass"
|
||||
:aria-label="t('zoomControls.label')"
|
||||
data-testid="zoom-controls-button"
|
||||
@@ -52,14 +56,16 @@
|
||||
|
||||
<Button
|
||||
v-tooltip.top="minimapTooltip"
|
||||
variant="secondary"
|
||||
severity="secondary"
|
||||
:aria-label="minimapTooltip"
|
||||
data-testid="toggle-minimap-button"
|
||||
:style="stringifiedMinimapStyles.buttonStyles"
|
||||
:class="minimapButtonClass"
|
||||
@click="onMinimapToggleClick"
|
||||
>
|
||||
<i class="icon-[lucide--map] h-4 w-4" />
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--map] h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@@ -71,25 +77,27 @@
|
||||
}
|
||||
}
|
||||
}"
|
||||
variant="secondary"
|
||||
severity="secondary"
|
||||
:class="linkVisibleClass"
|
||||
:aria-label="linkVisibilityAriaLabel"
|
||||
data-testid="toggle-link-visibility-button"
|
||||
:style="stringifiedMinimapStyles.buttonStyles"
|
||||
@click="onLinkVisibilityToggleClick"
|
||||
>
|
||||
<i class="icon-[lucide--route-off] h-4 w-4" />
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--route-off] h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import ButtonGroup from 'primevue/buttongroup'
|
||||
import { computed, onBeforeUnmount, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useZoomControls } from '@/composables/useZoomControls'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
|
||||
@@ -4,18 +4,21 @@
|
||||
value: $t('commands.Comfy_Canvas_ToggleSelectedNodes_Bypass.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('commands.Comfy_Canvas_ToggleSelectedNodes_Bypass.label')"
|
||||
severity="secondary"
|
||||
text
|
||||
data-testid="bypass-button"
|
||||
class="hover:bg-secondary-background"
|
||||
@click="toggleBypass"
|
||||
>
|
||||
<i class="icon-[lucide--redo-dot] size-4" />
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--redo-dot] h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
const commandStore = useCommandStore()
|
||||
|
||||
@@ -6,13 +6,19 @@
|
||||
showDelay: 1000
|
||||
}"
|
||||
data-testid="color-picker-button"
|
||||
variant="muted-textonly"
|
||||
:aria-label="t('g.color')"
|
||||
severity="secondary"
|
||||
text
|
||||
@click="() => (showColorPicker = !showColorPicker)"
|
||||
>
|
||||
<div class="flex items-center gap-1 px-0">
|
||||
<i class="pi pi-circle-fill" :style="{ color: currentColor ?? '' }" />
|
||||
<i class="icon-[lucide--chevron-down]" />
|
||||
<i
|
||||
class="pi pi-circle-fill h-4 w-4"
|
||||
:style="{ color: currentColor ?? '' }"
|
||||
/>
|
||||
<i
|
||||
class="pi pi-chevron-down h-4 w-4 py-1"
|
||||
:style="{ fontSize: '0.5rem' }"
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
<div
|
||||
@@ -42,12 +48,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import SelectButton from 'primevue/selectbutton'
|
||||
import type { Raw } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type {
|
||||
ColorOption as CanvasColorOption,
|
||||
Positionable
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
value: $t('commands.Comfy_Graph_EditSubgraphWidgets.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('commands.Comfy_Graph_EditSubgraphWidgets.label')"
|
||||
severity="secondary"
|
||||
text
|
||||
icon="icon-[lucide--settings-2]"
|
||||
@click="handleClick"
|
||||
>
|
||||
<i class="icon-[lucide--settings-2]" />
|
||||
</Button>
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
|
||||
|
||||
const rightSidePanelStore = useRightSidePanelStore()
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
value: $t('commands.Comfy_Graph_UnpackSubgraph.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('commands.Comfy_Graph_UnpackSubgraph.label')"
|
||||
severity="secondary"
|
||||
data-testid="convert-to-subgraph-button"
|
||||
text
|
||||
@click="() => commandStore.execute('Comfy.Graph.UnpackSubgraph')"
|
||||
>
|
||||
<i class="icon-[lucide--expand] size-4" />
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--expand] h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
<Button
|
||||
v-else-if="isConvertVisible"
|
||||
@@ -18,20 +20,21 @@
|
||||
value: $t('commands.Comfy_Graph_ConvertToSubgraph.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
size="icon"
|
||||
:aria-label="$t('commands.Comfy_Graph_ConvertToSubgraph.label')"
|
||||
severity="secondary"
|
||||
data-testid="convert-to-subgraph-button"
|
||||
text
|
||||
@click="() => commandStore.execute('Comfy.Graph.ConvertToSubgraph')"
|
||||
>
|
||||
<i class="icon-[lucide--shrink] size-4" />
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--shrink]" />
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useSelectionState } from '@/composables/graph/useSelectionState'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
value: $t('commands.Comfy_Canvas_DeleteSelectedItems.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('commands.Comfy_Canvas_DeleteSelectedItems.label')"
|
||||
severity="secondary"
|
||||
text
|
||||
icon-class="w-4 h-4"
|
||||
icon="pi pi-trash"
|
||||
data-testid="delete-button"
|
||||
@click="() => commandStore.execute('Comfy.Canvas.DeleteSelectedItems')"
|
||||
>
|
||||
<i class="icon-[lucide--trash-2]" />
|
||||
</Button>
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useSelectionState } from '@/composables/graph/useSelectionState'
|
||||
import type { Positionable } from '@/lib/litegraph/src/interfaces'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
@@ -4,21 +4,21 @@
|
||||
value: t('selectionToolbox.executeButton.tooltip'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="primary"
|
||||
:aria-label="t('selectionToolbox.executeButton.tooltip')"
|
||||
class="size-8 bg-primary-background text-white p-0"
|
||||
text
|
||||
@mouseenter="() => handleMouseEnter()"
|
||||
@mouseleave="() => handleMouseLeave()"
|
||||
@click="handleClick"
|
||||
>
|
||||
<i class="icon-[lucide--play]" />
|
||||
<i class="icon-[lucide--play] size-4" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useSelectionState } from '@/composables/graph/useSelectionState'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
|
||||
@@ -5,20 +5,17 @@
|
||||
st(`commands.${normalizeI18nKey(command.id)}.label`, '') || undefined,
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="st(`commands.${normalizeI18nKey(command.id)}.label`, '')"
|
||||
severity="secondary"
|
||||
text
|
||||
icon-class="w-4 h-4"
|
||||
:icon="typeof command.icon === 'function' ? command.icon() : command.icon"
|
||||
@click="() => commandStore.execute(command.id)"
|
||||
>
|
||||
<i
|
||||
:class="[
|
||||
typeof command.icon === 'function' ? command.icon() : command.icon
|
||||
]"
|
||||
/>
|
||||
</Button>
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { st } from '@/i18n'
|
||||
import type { ComfyCommand } from '@/stores/commandStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
@@ -4,16 +4,18 @@
|
||||
value: $t('g.frameNodes'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('g.frameNodes')"
|
||||
class="frame-nodes-button"
|
||||
text
|
||||
severity="secondary"
|
||||
@click="frameNodes"
|
||||
>
|
||||
<i class="icon-[lucide--frame]" />
|
||||
<i class="icon-[lucide--frame] h-4 w-4" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { useFrameNodes } from '@/composables/graph/useFrameNodes'
|
||||
|
||||
const { frameNodes } = useFrameNodes()
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
showDelay: 1000
|
||||
}"
|
||||
data-testid="info-button"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('g.info')"
|
||||
text
|
||||
severity="secondary"
|
||||
@click="onInfoClick"
|
||||
>
|
||||
<i class="icon-[lucide--info]" />
|
||||
<i class="icon-[lucide--info] size-4" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
|
||||
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
value: $t('commands.Comfy_3DViewer_Open3DViewer.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('commands.Comfy_3DViewer_Open3DViewer.label')"
|
||||
severity="secondary"
|
||||
text
|
||||
icon="pi pi-pencil"
|
||||
icon-class="w-4 h-4"
|
||||
@click="open3DViewer"
|
||||
>
|
||||
<i class="icon-[lucide--pencil]" />
|
||||
</Button>
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
const commandStore = useCommandStore()
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
value: $t('commands.Comfy_MaskEditor_OpenMaskEditor.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('commands.Comfy_MaskEditor_OpenMaskEditor.label')"
|
||||
severity="secondary"
|
||||
text
|
||||
@click="openMaskEditor"
|
||||
>
|
||||
<i class="icon-[comfy--mask]" />
|
||||
<i-comfy:mask class="!h-4 !w-4" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { useSelectionState } from '@/composables/graph/useSelectionState'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@
|
||||
showDelay: 1000
|
||||
}"
|
||||
data-testid="more-options-button"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('g.moreOptions')"
|
||||
text
|
||||
class="h-8 w-8 px-0"
|
||||
severity="secondary"
|
||||
@click="handleClick"
|
||||
>
|
||||
<i class="icon-[lucide--more-vertical]" />
|
||||
<i class="icon-[lucide--more-vertical] h-4 w-4" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { toggleNodeOptions } from '@/composables/graph/useMoreOptionsMenu'
|
||||
|
||||
const handleClick = (event: Event) => {
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
<Button
|
||||
v-show="isRefreshable"
|
||||
v-tooltip.top="t('g.refreshNode')"
|
||||
variant="muted-textonly"
|
||||
:aria-label="t('g.refreshNode')"
|
||||
severity="secondary"
|
||||
text
|
||||
data-testid="refresh-button"
|
||||
@click="refreshSelected"
|
||||
>
|
||||
<i class="icon-[lucide--refresh-cw]" />
|
||||
<i class="icon-[lucide--refresh-cw] h-4 w-4" />
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useRefreshableSelection } from '@/composables/useRefreshableSelection'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -5,18 +5,20 @@
|
||||
value: $t('commands.Comfy_PublishSubgraph.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
variant="muted-textonly"
|
||||
:aria-label="$t('commands.Comfy_PublishSubgraph.label')"
|
||||
severity="secondary"
|
||||
text
|
||||
@click="() => commandStore.execute('Comfy.PublishSubgraph')"
|
||||
>
|
||||
<i class="icon-[lucide--book-open]" />
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--book-open]" />
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { SubgraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
@@ -7,13 +7,7 @@
|
||||
@wheel.stop
|
||||
>
|
||||
<div class="show-menu relative">
|
||||
<Button
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
:aria-label="$t('menu.showMenu')"
|
||||
class="rounded-full"
|
||||
@click="toggleMenu"
|
||||
>
|
||||
<Button class="p-button-rounded p-button-text" @click="toggleMenu">
|
||||
<i class="pi pi-bars text-lg text-white" />
|
||||
</Button>
|
||||
|
||||
@@ -25,13 +19,8 @@
|
||||
<Button
|
||||
v-for="category in availableCategories"
|
||||
:key="category"
|
||||
variant="textonly"
|
||||
:class="
|
||||
cn(
|
||||
'flex w-full items-center justify-start',
|
||||
activeCategory === category && 'bg-smoke-600'
|
||||
)
|
||||
"
|
||||
class="p-button-text flex w-full items-center justify-start"
|
||||
:class="{ 'bg-smoke-600': activeCategory === category }"
|
||||
@click="selectCategory(category)"
|
||||
>
|
||||
<i :class="getCategoryIcon(category)" />
|
||||
@@ -83,6 +72,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import CameraControls from '@/components/load3d/controls/CameraControls.vue'
|
||||
@@ -90,14 +80,12 @@ import ExportControls from '@/components/load3d/controls/ExportControls.vue'
|
||||
import LightControls from '@/components/load3d/controls/LightControls.vue'
|
||||
import ModelControls from '@/components/load3d/controls/ModelControls.vue'
|
||||
import SceneControls from '@/components/load3d/controls/SceneControls.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type {
|
||||
CameraConfig,
|
||||
LightConfig,
|
||||
ModelConfig,
|
||||
SceneConfig
|
||||
} from '@/extensions/core/load3d/interfaces'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const { isSplatModel = false, isPlyModel = false } = defineProps<{
|
||||
isSplatModel?: boolean
|
||||
|
||||
@@ -72,10 +72,12 @@
|
||||
|
||||
<div class="p-4">
|
||||
<div class="flex gap-2">
|
||||
<Button variant="secondary" @click="handleCancel">
|
||||
<i class="pi pi-times" />
|
||||
{{ t('g.cancel') }}
|
||||
</Button>
|
||||
<Button
|
||||
icon="pi pi-times"
|
||||
severity="secondary"
|
||||
:label="t('g.cancel')"
|
||||
@click="handleCancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,6 +85,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { onBeforeUnmount, onMounted, ref, toRaw } from 'vue'
|
||||
|
||||
import CameraControls from '@/components/load3d/controls/viewer/ViewerCameraControls.vue'
|
||||
@@ -90,7 +93,6 @@ import ExportControls from '@/components/load3d/controls/viewer/ViewerExportCont
|
||||
import LightControls from '@/components/load3d/controls/viewer/ViewerLightControls.vue'
|
||||
import ModelControls from '@/components/load3d/controls/viewer/ViewerModelControls.vue'
|
||||
import SceneControls from '@/components/load3d/controls/viewer/ViewerSceneControls.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useLoad3dDrag } from '@/composables/useLoad3dDrag'
|
||||
import { useLoad3dViewer } from '@/composables/useLoad3dViewer'
|
||||
import { t } from '@/i18n'
|
||||
|
||||
@@ -3,13 +3,7 @@
|
||||
v-if="animations && animations.length > 0"
|
||||
class="pointer-events-auto absolute top-0 left-0 z-10 flex w-full items-center justify-center gap-2 pt-2"
|
||||
>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('g.playPause')"
|
||||
@click="togglePlay"
|
||||
>
|
||||
<Button class="p-button-rounded p-button-text" @click="togglePlay">
|
||||
<i
|
||||
:class="['pi', playing ? 'pi-pause' : 'pi-play', 'text-lg text-white']"
|
||||
/>
|
||||
@@ -34,10 +28,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Select from 'primevue/select'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
|
||||
type Animation = { name: string; index: number }
|
||||
|
||||
const animations = defineModel<Animation[]>('animations')
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.switchCamera'),
|
||||
showDelay: 300
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('load3d.switchCamera')"
|
||||
@click="switchCamera"
|
||||
>
|
||||
<i :class="['pi', 'pi-camera', 'text-lg text-white']" />
|
||||
<Button class="p-button-rounded p-button-text" @click="switchCamera">
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.switchCamera'),
|
||||
showDelay: 300
|
||||
}"
|
||||
:class="['pi', 'pi-camera', 'text-lg text-white']"
|
||||
/>
|
||||
</Button>
|
||||
<PopupSlider
|
||||
v-if="showFOVButton"
|
||||
@@ -22,10 +18,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import PopupSlider from '@/components/load3d/controls/PopupSlider.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type { CameraType } from '@/extensions/core/load3d/interfaces'
|
||||
|
||||
const cameraType = defineModel<CameraType>('cameraType')
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
<div class="flex flex-col">
|
||||
<div class="show-export-formats relative">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.exportModel'),
|
||||
showDelay: 300
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('load3d.exportModel')"
|
||||
class="p-button-rounded p-button-text"
|
||||
@click="toggleExportFormats"
|
||||
>
|
||||
<i class="pi pi-download text-lg text-white" />
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.exportModel'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-download text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
<div
|
||||
v-show="showExportFormats"
|
||||
@@ -22,8 +21,7 @@
|
||||
<Button
|
||||
v-for="format in exportFormats"
|
||||
:key="format.value"
|
||||
variant="textonly"
|
||||
class="text-white"
|
||||
class="p-button-text text-white"
|
||||
@click="exportModel(format.value)"
|
||||
>
|
||||
{{ format.label }}
|
||||
@@ -35,10 +33,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'exportModel', format: string): void
|
||||
}>()
|
||||
@@ -51,17 +48,17 @@ const exportFormats = [
|
||||
{ label: 'STL', value: 'stl' }
|
||||
]
|
||||
|
||||
function toggleExportFormats() {
|
||||
const toggleExportFormats = () => {
|
||||
showExportFormats.value = !showExportFormats.value
|
||||
}
|
||||
|
||||
function exportModel(format: string) {
|
||||
const exportModel = (format: string) => {
|
||||
emit('exportModel', format)
|
||||
|
||||
showExportFormats.value = false
|
||||
}
|
||||
|
||||
function closeExportFormatsList(e: MouseEvent) {
|
||||
const closeExportFormatsList = (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement
|
||||
|
||||
if (!target.closest('.show-export-formats')) {
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
<div class="flex flex-col">
|
||||
<div v-if="showLightIntensityButton" class="show-light-intensity relative">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.lightIntensity'),
|
||||
showDelay: 300
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('load3d.lightIntensity')"
|
||||
class="p-button-rounded p-button-text"
|
||||
@click="toggleLightIntensity"
|
||||
>
|
||||
<i class="pi pi-sun text-lg text-white" />
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.lightIntensity'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-sun text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
<div
|
||||
v-show="showLightIntensity"
|
||||
@@ -32,10 +31,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Slider from 'primevue/slider'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type { MaterialMode } from '@/extensions/core/load3d/interfaces'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
|
||||
@@ -57,11 +56,11 @@ const lightAdjustmentIncrement = useSettingStore().get(
|
||||
'Comfy.Load3D.LightAdjustmentIncrement'
|
||||
)
|
||||
|
||||
function toggleLightIntensity() {
|
||||
const toggleLightIntensity = () => {
|
||||
showLightIntensity.value = !showLightIntensity.value
|
||||
}
|
||||
|
||||
function closeLightSlider(e: MouseEvent) {
|
||||
const closeLightSlider = (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement
|
||||
|
||||
if (!target.closest('.show-light-intensity')) {
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<div class="show-up-direction relative">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: t('load3d.upDirection'),
|
||||
showDelay: 300
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="t('load3d.upDirection')"
|
||||
@click="toggleUpDirection"
|
||||
>
|
||||
<i class="pi pi-arrow-up text-lg text-white" />
|
||||
<Button class="p-button-rounded p-button-text" @click="toggleUpDirection">
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: t('load3d.upDirection'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-arrow-up text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
<div
|
||||
v-show="showUpDirection"
|
||||
@@ -22,10 +18,8 @@
|
||||
<Button
|
||||
v-for="direction in upDirections"
|
||||
:key="direction"
|
||||
variant="textonly"
|
||||
:class="
|
||||
cn('text-white', upDirection === direction && 'bg-blue-500')
|
||||
"
|
||||
class="p-button-text text-white"
|
||||
:class="{ 'bg-blue-500': upDirection === direction }"
|
||||
@click="selectUpDirection(direction)"
|
||||
>
|
||||
{{ direction.toUpperCase() }}
|
||||
@@ -36,17 +30,16 @@
|
||||
|
||||
<div v-if="!hideMaterialMode" class="show-material-mode relative">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: t('load3d.materialMode'),
|
||||
showDelay: 300
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="t('load3d.materialMode')"
|
||||
class="p-button-rounded p-button-text"
|
||||
@click="toggleMaterialMode"
|
||||
>
|
||||
<i class="pi pi-box text-lg text-white" />
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: t('load3d.materialMode'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-box text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
<div
|
||||
v-show="showMaterialMode"
|
||||
@@ -56,13 +49,8 @@
|
||||
<Button
|
||||
v-for="mode in materialModes"
|
||||
:key="mode"
|
||||
variant="textonly"
|
||||
:class="
|
||||
cn(
|
||||
'whitespace-nowrap text-white',
|
||||
materialMode === mode && 'bg-blue-500'
|
||||
)
|
||||
"
|
||||
class="p-button-text whitespace-nowrap text-white"
|
||||
:class="{ 'bg-blue-500': materialMode === mode }"
|
||||
@click="selectMaterialMode(mode)"
|
||||
>
|
||||
{{ formatMaterialMode(mode) }}
|
||||
@@ -74,15 +62,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type {
|
||||
MaterialMode,
|
||||
UpDirection
|
||||
} from '@/extensions/core/load3d/interfaces'
|
||||
import { t } from '@/i18n'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const { hideMaterialMode = false, isPlyModel = false } = defineProps<{
|
||||
hideMaterialMode?: boolean
|
||||
@@ -121,31 +108,31 @@ const materialModes = computed(() => {
|
||||
return modes
|
||||
})
|
||||
|
||||
function toggleUpDirection() {
|
||||
const toggleUpDirection = () => {
|
||||
showUpDirection.value = !showUpDirection.value
|
||||
showMaterialMode.value = false
|
||||
}
|
||||
|
||||
function selectUpDirection(direction: UpDirection) {
|
||||
const selectUpDirection = (direction: UpDirection) => {
|
||||
upDirection.value = direction
|
||||
showUpDirection.value = false
|
||||
}
|
||||
|
||||
function toggleMaterialMode() {
|
||||
const toggleMaterialMode = () => {
|
||||
showMaterialMode.value = !showMaterialMode.value
|
||||
showUpDirection.value = false
|
||||
}
|
||||
|
||||
function selectMaterialMode(mode: MaterialMode) {
|
||||
const selectMaterialMode = (mode: MaterialMode) => {
|
||||
materialMode.value = mode
|
||||
showMaterialMode.value = false
|
||||
}
|
||||
|
||||
function formatMaterialMode(mode: MaterialMode) {
|
||||
const formatMaterialMode = (mode: MaterialMode) => {
|
||||
return t(`load3d.materialModes.${mode}`)
|
||||
}
|
||||
|
||||
function closeSceneSlider(e: MouseEvent) {
|
||||
const closeSceneSlider = (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement
|
||||
|
||||
if (!target.closest('.show-up-direction')) {
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
<template>
|
||||
<div class="relative show-slider">
|
||||
<Button
|
||||
v-tooltip.right="{ value: tooltipText, showDelay: 300 }"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="tooltipText"
|
||||
@click="toggleSlider"
|
||||
>
|
||||
<i :class="['pi', icon, 'text-lg text-white']" />
|
||||
<Button class="p-button-rounded p-button-text" @click="toggleSlider">
|
||||
<i
|
||||
v-tooltip.right="{ value: tooltipText, showDelay: 300 }"
|
||||
:class="['pi', icon, 'text-lg text-white']"
|
||||
/>
|
||||
</Button>
|
||||
<div
|
||||
v-show="showSlider"
|
||||
@@ -26,11 +22,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Slider from 'primevue/slider'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
|
||||
const {
|
||||
icon = 'pi-expand',
|
||||
min = 10,
|
||||
|
||||
@@ -2,26 +2,20 @@
|
||||
<div class="relative rounded-lg bg-smoke-700/30">
|
||||
<div class="flex flex-col gap-2">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: isRecording
|
||||
? $t('load3d.stopRecording')
|
||||
: $t('load3d.startRecording'),
|
||||
showDelay: 300
|
||||
class="p-button-rounded p-button-text"
|
||||
:class="{
|
||||
'p-button-danger': isRecording,
|
||||
'recording-button-blink': isRecording
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
:class="
|
||||
cn(
|
||||
'rounded-full',
|
||||
isRecording && 'text-red-500 recording-button-blink'
|
||||
)
|
||||
"
|
||||
:aria-label="
|
||||
isRecording ? $t('load3d.stopRecording') : $t('load3d.startRecording')
|
||||
"
|
||||
@click="toggleRecording"
|
||||
>
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: isRecording
|
||||
? $t('load3d.stopRecording')
|
||||
: $t('load3d.startRecording'),
|
||||
showDelay: 300
|
||||
}"
|
||||
:class="[
|
||||
'pi',
|
||||
isRecording ? 'pi-circle-fill' : 'pi-video',
|
||||
@@ -32,32 +26,30 @@
|
||||
|
||||
<Button
|
||||
v-if="hasRecording && !isRecording"
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.exportRecording'),
|
||||
showDelay: 300
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('load3d.exportRecording')"
|
||||
class="p-button-rounded p-button-text"
|
||||
@click="handleExportRecording"
|
||||
>
|
||||
<i class="pi pi-download text-lg text-white" />
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.exportRecording'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-download text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
v-if="hasRecording && !isRecording"
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.clearRecording'),
|
||||
showDelay: 300
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('load3d.clearRecording')"
|
||||
class="p-button-rounded p-button-text"
|
||||
@click="handleClearRecording"
|
||||
>
|
||||
<i class="pi pi-trash text-lg text-white" />
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.clearRecording'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-trash text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<div
|
||||
@@ -71,8 +63,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
const hasRecording = defineModel<boolean>('hasRecording')
|
||||
const isRecording = defineModel<boolean>('isRecording')
|
||||
@@ -85,7 +76,7 @@ const emit = defineEmits<{
|
||||
(e: 'clearRecording'): void
|
||||
}>()
|
||||
|
||||
function toggleRecording() {
|
||||
const toggleRecording = () => {
|
||||
if (isRecording.value) {
|
||||
emit('stopRecording')
|
||||
} else {
|
||||
@@ -93,15 +84,15 @@ function toggleRecording() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleExportRecording() {
|
||||
const handleExportRecording = () => {
|
||||
emit('exportRecording')
|
||||
}
|
||||
|
||||
function handleClearRecording() {
|
||||
const handleClearRecording = () => {
|
||||
emit('clearRecording')
|
||||
}
|
||||
|
||||
function formatDuration(seconds: number): string {
|
||||
const formatDuration = (seconds: number): string => {
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const remainingSeconds = Math.floor(seconds % 60)
|
||||
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<Button
|
||||
v-tooltip.right="{ value: $t('load3d.showGrid'), showDelay: 300 }"
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
:class="cn('rounded-full', showGrid && 'ring-2 ring-white/50')"
|
||||
:aria-label="$t('load3d.showGrid')"
|
||||
class="p-button-rounded p-button-text"
|
||||
:class="{ 'p-button-outlined': showGrid }"
|
||||
@click="toggleGrid"
|
||||
>
|
||||
<i class="pi pi-table text-lg text-white" />
|
||||
<i
|
||||
v-tooltip.right="{ value: $t('load3d.showGrid'), showDelay: 300 }"
|
||||
class="pi pi-table text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<div v-if="!hasBackgroundImage">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.backgroundColor'),
|
||||
showDelay: 300
|
||||
}"
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('load3d.backgroundColor')"
|
||||
@click="openColorPicker"
|
||||
>
|
||||
<i class="pi pi-palette text-lg text-white" />
|
||||
<Button class="p-button-rounded p-button-text" @click="openColorPicker">
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.backgroundColor'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-palette text-lg text-white"
|
||||
/>
|
||||
<input
|
||||
ref="colorPickerRef"
|
||||
type="color"
|
||||
@@ -37,18 +33,14 @@
|
||||
</div>
|
||||
|
||||
<div v-if="!hasBackgroundImage">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.uploadBackgroundImage'),
|
||||
showDelay: 300
|
||||
}"
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('load3d.uploadBackgroundImage')"
|
||||
@click="openImagePicker"
|
||||
>
|
||||
<i class="pi pi-image text-lg text-white" />
|
||||
<Button class="p-button-rounded p-button-text" @click="openImagePicker">
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.uploadBackgroundImage'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-image text-lg text-white"
|
||||
/>
|
||||
<input
|
||||
ref="imagePickerRef"
|
||||
type="file"
|
||||
@@ -61,22 +53,17 @@
|
||||
|
||||
<div v-if="hasBackgroundImage">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.panoramaMode'),
|
||||
showDelay: 300
|
||||
}"
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
:class="
|
||||
cn(
|
||||
'rounded-full',
|
||||
backgroundRenderMode === 'panorama' && 'ring-2 ring-white/50'
|
||||
)
|
||||
"
|
||||
:aria-label="$t('load3d.panoramaMode')"
|
||||
class="p-button-rounded p-button-text"
|
||||
:class="{ 'p-button-outlined': backgroundRenderMode === 'panorama' }"
|
||||
@click="toggleBackgroundRenderMode"
|
||||
>
|
||||
<i class="pi pi-globe text-lg text-white" />
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.panoramaMode'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-globe text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -88,29 +75,27 @@
|
||||
|
||||
<div v-if="hasBackgroundImage">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.removeBackgroundImage'),
|
||||
showDelay: 300
|
||||
}"
|
||||
variant="textonly"
|
||||
size="icon"
|
||||
class="rounded-full"
|
||||
:aria-label="$t('load3d.removeBackgroundImage')"
|
||||
class="p-button-rounded p-button-text"
|
||||
@click="removeBackgroundImage"
|
||||
>
|
||||
<i class="pi pi-times text-lg text-white" />
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: $t('load3d.removeBackgroundImage'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-times text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import PopupSlider from '@/components/load3d/controls/PopupSlider.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type { BackgroundRenderModeType } from '@/extensions/core/load3d/interfaces'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'updateBackgroundImage', file: File | null): void
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
<template>
|
||||
<div class="relative rounded-lg bg-smoke-700/30">
|
||||
<div class="flex flex-col gap-2">
|
||||
<Button
|
||||
v-tooltip.right="{
|
||||
value: t('load3d.openIn3DViewer'),
|
||||
showDelay: 300
|
||||
}"
|
||||
size="icon"
|
||||
variant="textonly"
|
||||
class="rounded-full"
|
||||
:aria-label="t('load3d.openIn3DViewer')"
|
||||
@click="openIn3DViewer"
|
||||
>
|
||||
<i class="pi pi-expand text-lg text-white" />
|
||||
<Button class="p-button-rounded p-button-text" @click="openIn3DViewer">
|
||||
<i
|
||||
v-tooltip.right="{
|
||||
value: t('load3d.openIn3DViewer'),
|
||||
showDelay: 300
|
||||
}"
|
||||
class="pi pi-expand text-lg text-white"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import Load3DViewerContent from '@/components/load3d/Load3dViewerContent.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { t } from '@/i18n'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import { useLoad3dService } from '@/services/load3dService'
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
</Select>
|
||||
|
||||
<Button
|
||||
variant="muted-textonly"
|
||||
class="rounded-full"
|
||||
severity="secondary"
|
||||
text
|
||||
rounded
|
||||
@click="exportModel(exportFormat)"
|
||||
>
|
||||
{{ $t('load3d.export') }}
|
||||
@@ -19,11 +20,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Select from 'primevue/select'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'exportModel', format: string): void
|
||||
}>()
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
</div>
|
||||
|
||||
<div v-if="!hasBackgroundImage && !disableBackgroundUpload">
|
||||
<Button variant="secondary" class="w-full" @click="openImagePicker">
|
||||
<i class="pi pi-image" />
|
||||
{{ $t('load3d.uploadBackgroundImage') }}
|
||||
</Button>
|
||||
<Button
|
||||
severity="secondary"
|
||||
:label="$t('load3d.uploadBackgroundImage')"
|
||||
icon="pi pi-image"
|
||||
class="w-full"
|
||||
@click="openImagePicker"
|
||||
/>
|
||||
<input
|
||||
ref="imagePickerRef"
|
||||
type="file"
|
||||
@@ -31,38 +34,38 @@
|
||||
<div v-if="hasBackgroundImage" class="space-y-2">
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
:variant="backgroundRenderMode === 'tiled' ? 'primary' : 'secondary'"
|
||||
:severity="backgroundRenderMode === 'tiled' ? 'primary' : 'secondary'"
|
||||
:label="$t('load3d.tiledMode')"
|
||||
icon="pi pi-th-large"
|
||||
class="flex-1"
|
||||
@click="setBackgroundRenderMode('tiled')"
|
||||
>
|
||||
<i class="pi pi-th-large" />
|
||||
{{ $t('load3d.tiledMode') }}
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
:variant="
|
||||
:severity="
|
||||
backgroundRenderMode === 'panorama' ? 'primary' : 'secondary'
|
||||
"
|
||||
:label="$t('load3d.panoramaMode')"
|
||||
icon="pi pi-globe"
|
||||
class="flex-1"
|
||||
@click="setBackgroundRenderMode('panorama')"
|
||||
>
|
||||
<i class="pi pi-globe" />
|
||||
{{ $t('load3d.panoramaMode') }}
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
<Button variant="secondary" class="w-full" @click="removeBackgroundImage">
|
||||
<i class="pi pi-times" />
|
||||
{{ $t('load3d.removeBackgroundImage') }}
|
||||
</Button>
|
||||
<Button
|
||||
severity="secondary"
|
||||
:label="$t('load3d.removeBackgroundImage')"
|
||||
icon="pi pi-times"
|
||||
class="w-full"
|
||||
@click="removeBackgroundImage"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import Checkbox from 'primevue/checkbox'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
|
||||
const backgroundColor = defineModel<string>('backgroundColor')
|
||||
const showGrid = defineModel<boolean>('showGrid')
|
||||
const backgroundRenderMode = defineModel<'tiled' | 'panorama'>(
|
||||
|
||||
@@ -46,22 +46,28 @@
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3">
|
||||
<Button variant="primary" :disabled="!saveEnabled" @click="handleSave">
|
||||
<i class="pi pi-check" />
|
||||
{{ saveButtonText }}
|
||||
</Button>
|
||||
<Button variant="secondary" @click="handleCancel">
|
||||
<i class="pi pi-times" />
|
||||
{{ t('g.cancel') }}
|
||||
</Button>
|
||||
<Button
|
||||
:label="saveButtonText"
|
||||
icon="pi pi-check"
|
||||
size="small"
|
||||
:disabled="!saveEnabled"
|
||||
@click="handleSave"
|
||||
/>
|
||||
<Button
|
||||
:label="t('g.cancel')"
|
||||
icon="pi pi-times"
|
||||
size="small"
|
||||
severity="secondary"
|
||||
@click="handleCancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useCanvasTools } from '@/composables/maskeditor/useCanvasTools'
|
||||
import { useMaskEditorSaver } from '@/composables/maskeditor/useMaskEditorSaver'
|
||||
import { t } from '@/i18n'
|
||||
|
||||
@@ -34,7 +34,7 @@ function resetStores() {
|
||||
queue.runningTasks = []
|
||||
queue.historyTasks = []
|
||||
|
||||
exec.nodeProgressStatesByPrompt = {}
|
||||
exec.setNodeProgressStatesByPrompt({})
|
||||
}
|
||||
|
||||
function makePendingTask(
|
||||
@@ -146,7 +146,7 @@ export const Queued: Story = {
|
||||
// Queued at (in metadata on prompt[4])
|
||||
|
||||
// One running workflow
|
||||
exec.nodeProgressStatesByPrompt = {
|
||||
exec.setNodeProgressStatesByPrompt({
|
||||
p1: {
|
||||
'1': {
|
||||
value: 1,
|
||||
@@ -156,7 +156,7 @@ export const Queued: Story = {
|
||||
prompt_id: 'p1'
|
||||
}
|
||||
}
|
||||
} as any
|
||||
} as any)
|
||||
|
||||
return { args: { ...args, jobId } }
|
||||
},
|
||||
@@ -196,7 +196,7 @@ export const QueuedParallel: Story = {
|
||||
]
|
||||
|
||||
// Two parallel workflows running
|
||||
exec.nodeProgressStatesByPrompt = {
|
||||
exec.setNodeProgressStatesByPrompt({
|
||||
p1: {
|
||||
'1': {
|
||||
value: 1,
|
||||
@@ -215,7 +215,7 @@ export const QueuedParallel: Story = {
|
||||
prompt_id: 'p2'
|
||||
}
|
||||
}
|
||||
} as any
|
||||
} as any)
|
||||
|
||||
return { args: { ...args, jobId } }
|
||||
},
|
||||
@@ -246,7 +246,7 @@ export const Running: Story = {
|
||||
makeHistoryTask('hist-r3', 252, 60, true)
|
||||
]
|
||||
|
||||
exec.nodeProgressStatesByPrompt = {
|
||||
exec.setNodeProgressStatesByPrompt({
|
||||
p1: {
|
||||
'1': {
|
||||
value: 5,
|
||||
@@ -256,7 +256,7 @@ export const Running: Story = {
|
||||
prompt_id: 'p1'
|
||||
}
|
||||
}
|
||||
} as any
|
||||
} as any)
|
||||
|
||||
return { args: { ...args, jobId } }
|
||||
},
|
||||
@@ -291,7 +291,7 @@ export const QueuedZeroAheadSingleRunning: Story = {
|
||||
|
||||
queue.runningTasks = [makeRunningTaskWithStart('running-1', 505, 20)]
|
||||
|
||||
exec.nodeProgressStatesByPrompt = {
|
||||
exec.setNodeProgressStatesByPrompt({
|
||||
p1: {
|
||||
'1': {
|
||||
value: 1,
|
||||
@@ -301,7 +301,7 @@ export const QueuedZeroAheadSingleRunning: Story = {
|
||||
prompt_id: 'p1'
|
||||
}
|
||||
}
|
||||
} as any
|
||||
} as any)
|
||||
|
||||
return { args: { ...args, jobId } }
|
||||
},
|
||||
@@ -339,7 +339,7 @@ export const QueuedZeroAheadMultiRunning: Story = {
|
||||
makeRunningTaskWithStart('running-b', 507, 10)
|
||||
]
|
||||
|
||||
exec.nodeProgressStatesByPrompt = {
|
||||
exec.setNodeProgressStatesByPrompt({
|
||||
p1: {
|
||||
'1': {
|
||||
value: 2,
|
||||
@@ -358,7 +358,7 @@ export const QueuedZeroAheadMultiRunning: Story = {
|
||||
prompt_id: 'p2'
|
||||
}
|
||||
}
|
||||
} as any
|
||||
} as any)
|
||||
|
||||
return { args: { ...args, jobId } }
|
||||
},
|
||||
|
||||