mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
[Extension] Selection toolbox API (#2672)
This commit is contained in:
29
README.md
29
README.md
@@ -468,6 +468,35 @@ We will support custom icons later.
|
||||

|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>v1.10.9: Selection Toolbox API</summary>
|
||||
|
||||
Extensions can register commands that appear in the selection toolbox when specific items are selected on the canvas.
|
||||
|
||||
```js
|
||||
app.registerExtension({
|
||||
name: 'TestExtension1',
|
||||
commands: [
|
||||
{
|
||||
id: 'test.selection.command',
|
||||
label: 'Test Command',
|
||||
icon: 'pi pi-star',
|
||||
function: () => {
|
||||
// Command logic here
|
||||
}
|
||||
}
|
||||
],
|
||||
// Return an array of command IDs to show in the selection toolbox
|
||||
// when an item is selected
|
||||
getSelectionToolboxCommands: (selectedItem) => ['test.selection.command']
|
||||
})
|
||||
```
|
||||
|
||||
The selection toolbox will display the command button when items are selected:
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
## Development
|
||||
|
||||
### Tech Stack
|
||||
|
||||
@@ -298,4 +298,45 @@ test.describe('Topbar commands', () => {
|
||||
expect(await comfyPage.page.evaluate(() => window['value'])).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Selection Toolbox', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
})
|
||||
|
||||
test('Should allow adding commands to selection toolbox', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Register an extension with a selection toolbox command
|
||||
await comfyPage.page.evaluate(() => {
|
||||
window['app'].registerExtension({
|
||||
name: 'TestExtension1',
|
||||
commands: [
|
||||
{
|
||||
id: 'test.selection.command',
|
||||
label: 'Test Command',
|
||||
icon: 'pi pi-star',
|
||||
function: () => {
|
||||
window['selectionCommandExecuted'] = true
|
||||
}
|
||||
}
|
||||
],
|
||||
getSelectionToolboxCommands: () => ['test.selection.command']
|
||||
})
|
||||
})
|
||||
|
||||
await comfyPage.selectNodes(['CLIP Text Encode (Prompt)'])
|
||||
|
||||
// Click the command button in the selection toolbox
|
||||
const toolboxButton = comfyPage.page.locator(
|
||||
'.selection-toolbox button:has(.pi-star)'
|
||||
)
|
||||
await toolboxButton.click()
|
||||
|
||||
// Verify the command was executed
|
||||
expect(
|
||||
await comfyPage.page.evaluate(() => window['selectionCommandExecuted'])
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,6 +40,14 @@
|
||||
icon="pi pi-refresh"
|
||||
@click="refreshSelected"
|
||||
/>
|
||||
<Button
|
||||
v-for="command in extensionToolboxCommands"
|
||||
:key="command.id"
|
||||
severity="secondary"
|
||||
text
|
||||
:icon="typeof command.icon === 'function' ? command.icon() : command.icon"
|
||||
@click="() => commandStore.execute(command.id)"
|
||||
/>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
@@ -50,12 +58,14 @@ import { computed } from 'vue'
|
||||
|
||||
import ColorPickerButton from '@/components/graph/selectionToolbox/ColorPickerButton.vue'
|
||||
import { useRefreshableSelection } from '@/composables/useRefreshableSelection'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useExtensionService } from '@/services/extensionService'
|
||||
import { ComfyCommand, useCommandStore } from '@/stores/commandStore'
|
||||
import { useCanvasStore } from '@/stores/graphStore'
|
||||
import { isLGraphGroup, isLGraphNode } from '@/utils/litegraphUtil'
|
||||
|
||||
const commandStore = useCommandStore()
|
||||
const canvasStore = useCanvasStore()
|
||||
const extensionService = useExtensionService()
|
||||
const { isRefreshable, refreshSelected } = useRefreshableSelection()
|
||||
const nodeSelected = computed(() =>
|
||||
canvasStore.selectedItems.some(isLGraphNode)
|
||||
@@ -63,6 +73,22 @@ const nodeSelected = computed(() =>
|
||||
const groupSelected = computed(() =>
|
||||
canvasStore.selectedItems.some(isLGraphGroup)
|
||||
)
|
||||
|
||||
const extensionToolboxCommands = computed<ComfyCommand[]>(() => {
|
||||
const commandIds = new Set<string>(
|
||||
canvasStore.selectedItems
|
||||
.map(
|
||||
(item) =>
|
||||
extensionService
|
||||
.invokeExtensions('getSelectionToolboxCommands', item)
|
||||
.flat() as string[]
|
||||
)
|
||||
.flat()
|
||||
)
|
||||
return Array.from(commandIds)
|
||||
.map((commandId) => commandStore.getCommand(commandId))
|
||||
.filter((command) => command !== undefined)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||
import { Positionable } from '@comfyorg/litegraph/dist/interfaces'
|
||||
|
||||
import type { ComfyApp } from '@/scripts/app'
|
||||
import type { ComfyWidgetConstructor } from '@/scripts/widgets'
|
||||
@@ -97,6 +98,14 @@ export interface ComfyExtension {
|
||||
* @returns An array of {[widget name]: widget data}
|
||||
*/
|
||||
getCustomWidgets?(app: ComfyApp): Promise<Widgets> | Widgets
|
||||
|
||||
/**
|
||||
* Allows the extension to add additional commands to the selection toolbox
|
||||
* @param selectedItem The selected item on the canvas
|
||||
* @returns An array of command ids to add to the selection toolbox
|
||||
*/
|
||||
getSelectionToolboxCommands?(selectedItem: Positionable): string[]
|
||||
|
||||
/**
|
||||
* Allows the extension to add additional handling to the node before it is registered with **LGraph**
|
||||
* @param nodeType The node class (not an instance)
|
||||
|
||||
Reference in New Issue
Block a user