From 3d09f89251ce3e96d375eee897d3a8834f83c8d1 Mon Sep 17 00:00:00 2001 From: Connor Byrne Date: Wed, 20 May 2026 20:07:13 -0700 Subject: [PATCH] docs(ext-api): refresh @example blocks + deprecate notify (W17.C/D/E) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - W17.C: refresh 5 stale @example blocks (types.ts, lifecycle.ts, node.ts, imperatives.ts ×2) — replace deprecated `async setup()` with `setup() { onMounted(...) }`; remove A1-removed `node.getWidget` from defineNode + NodeHandle.on migration examples; align imperatives examples with W17.B notify-toast verdict. - W17.D: author ~37 new @example blocks across 9 files for sparse exports surfaced by W17.A audit. Pure JSDoc — zero exported-type diff. - W17.E: add @deprecated JSDoc to notify() + NotifyOptions per D-notify-toast-consolidation (W17.B verdict (a) soft-deprecate). Pure JSDoc — no signature, type, or export changes. Phase A surface remains FROZEN at ee0537fdb5 (foundation surface SHA unchanged; only @example / @deprecated text differs). See research/dashboard-snippet-audit-2026-05-21.md (W17.A) for full per-edit rationale. --- src/extension-api/events.ts | 62 ++++++++++++++++++++++++++++++ src/extension-api/identifiers.ts | 33 ++++++++++++++++ src/extension-api/imperatives.ts | 18 +++++++-- src/extension-api/lifecycle.ts | 40 ++++++++++++++----- src/extension-api/node.ts | 35 +++++++++++++---- src/extension-api/registrations.ts | 14 +++++++ src/extension-api/shell.ts | 36 +++++++++++++++++ src/extension-api/types.ts | 8 ++-- src/extension-api/widget.ts | 34 ++++++++++++++++ 9 files changed, 256 insertions(+), 24 deletions(-) diff --git a/src/extension-api/events.ts b/src/extension-api/events.ts index 67d6fccf20..a4ee02c637 100644 --- a/src/extension-api/events.ts +++ b/src/extension-api/events.ts @@ -22,6 +22,15 @@ export type Handler = (event: E) => void * support async handling (currently only `beforeSerialize`). * * @typeParam E - The event payload type. + * @example + * ```ts + * import type { AsyncHandler, WidgetBeforeSerializeEvent } from '@comfyorg/extension-api' + * + * const handler: AsyncHandler = async (e) => { + * const frame = await captureFrame() + * e.setSerializedValue(frame) + * } + * ``` */ export type AsyncHandler = (event: E) => void | Promise @@ -76,12 +85,24 @@ import { getCurrentExtensionInstance } from '@/services/extension-api-service' export interface GraphEventPayloads { [event: string]: unknown } +/** + * See {@link GraphEventPayloads | the augmentation example} — + * augment this interface the same way to narrow `execution.*` payloads. + */ export interface ExecutionEventPayloads { [event: string]: unknown } +/** + * See {@link GraphEventPayloads | the augmentation example} — + * augment this interface the same way to narrow `server.*` payloads. + */ export interface ServerEventPayloads { [event: string]: unknown } +/** + * See {@link GraphEventPayloads | the augmentation example} — + * augment this interface the same way to narrow `workbench.*` payloads. + */ export interface WorkbenchEventPayloads { [event: string]: unknown } @@ -179,6 +200,21 @@ export const graph: EventNamespace = makeNamespace( * * @publicAPI * @stability experimental + * @example + * ```ts + * import { defineExtension, onMounted, execution } from '@comfyorg/extension-api' + * + * defineExtension({ + * name: 'my-ext', + * setup() { + * onMounted(() => { + * execution.on('start', (e) => console.log('run started', e)) + * execution.on('progress', (e) => console.log('progress', e)) + * execution.on('end', () => console.log('run done')) + * }) + * } + * }) + * ``` */ export const execution: EventNamespace = makeNamespace( (evt) => `execution_${evt}` @@ -194,6 +230,19 @@ export const execution: EventNamespace = makeNamespace( * * @publicAPI * @stability experimental + * @example + * ```ts + * import { defineExtension, onMounted, server } from '@comfyorg/extension-api' + * + * defineExtension({ + * name: 'my-ext', + * setup() { + * onMounted(() => { + * server.on('reconnected', () => console.log('server back online')) + * }) + * } + * }) + * ``` */ export const server: EventNamespace = makeNamespace( (evt) => evt @@ -209,6 +258,19 @@ export const server: EventNamespace = makeNamespace( * * @publicAPI * @stability experimental + * @example + * ```ts + * import { defineExtension, onMounted, workbench } from '@comfyorg/extension-api' + * + * defineExtension({ + * name: 'my-ext', + * setup() { + * onMounted(() => { + * workbench.on('notification', (e) => console.log('workbench notif', e)) + * }) + * } + * }) + * ``` */ export const workbench: EventNamespace = makeNamespace( (evt) => `workbench:${evt}` diff --git a/src/extension-api/identifiers.ts b/src/extension-api/identifiers.ts index 3c6d228f7c..334990b1a8 100644 --- a/src/extension-api/identifiers.ts +++ b/src/extension-api/identifiers.ts @@ -11,6 +11,39 @@ export type { NodeLocatorId, NodeExecutionId } from '@/types/nodeIdentification' +/** + * Node identity round-trip helpers. Create/parse branded `NodeLocatorId` and + * `NodeExecutionId` values, or narrow an `unknown` to one with the type + * guards. Use these instead of raw string manipulation so future changes to + * the identity scheme stay transparent. + * + * @example + * ```ts + * import { + * createNodeLocatorId, + * parseNodeLocatorId, + * isNodeLocatorId, + * createNodeExecutionId, + * parseNodeExecutionId, + * isNodeExecutionId + * } from '@comfyorg/extension-api' + * + * // Construct + * const locator = createNodeLocatorId(graphUuid, localId) + * const execId = createNodeExecutionId(locator, runTag) + * + * // Narrow + * if (isNodeLocatorId(maybe)) { + * const parts = parseNodeLocatorId(maybe) + * console.log(parts.graphUuid, parts.localId) + * } + * + * if (isNodeExecutionId(maybe)) { + * const parts = parseNodeExecutionId(maybe) + * console.log(parts.locator, parts.runTag) + * } + * ``` + */ export { isNodeLocatorId, isNodeExecutionId, diff --git a/src/extension-api/imperatives.ts b/src/extension-api/imperatives.ts index 60f90e5ef3..2df4fd7b26 100644 --- a/src/extension-api/imperatives.ts +++ b/src/extension-api/imperatives.ts @@ -16,8 +16,7 @@ * import { * defineExtension, * onMounted, - * toast, - * notify + * toast * } from '@comfyorg/extension-api' * * defineExtension({ @@ -25,7 +24,6 @@ * setup() { * onMounted(() => { * toast.show({ severity: 'info', summary: 'Ready' }) - * notify({ kind: 'info', message: 'Loaded' }) * }) * } * }) @@ -44,6 +42,8 @@ import type { ToastMessageOptions } from '@/types/extensionTypes' * {@link toast}. `kind` maps onto PrimeVue toast severities; `message` maps * to `summary`; `detail` is optional supplementary text. * + * @deprecated Use {@link ToastMessageOptions} via `toast.show(...)`. See + * D-notify-toast-consolidation. * @publicAPI * @stability experimental */ @@ -100,11 +100,21 @@ export const toast: { * * Fire-and-forget. * + * @deprecated Use {@link toast.show} — `notify` is a 1:1 wrapper sharing the + * same transport. See D-notify-toast-consolidation. * @publicAPI * @stability experimental * @example * ```ts - * notify({ kind: 'error', message: 'Workflow failed', detail: err.message }) + * // `notify` is deprecated — prefer `toast.show` directly. See + * // D-notify-toast-consolidation. + * import { toast } from '@comfyorg/extension-api' + * + * toast.show({ + * severity: 'error', + * summary: 'Workflow failed', + * detail: err.message + * }) * ``` */ export function notify(opts: NotifyOptions): void { diff --git a/src/extension-api/lifecycle.ts b/src/extension-api/lifecycle.ts index 1eddf091dc..647610f093 100644 --- a/src/extension-api/lifecycle.ts +++ b/src/extension-api/lifecycle.ts @@ -57,19 +57,17 @@ import type { * ```ts * import { defineNode } from '@comfyorg/extension-api' * - * // Assumes the Python `PreviewAny` node declares a hidden, read-only - * // STRING input named `preview` in INPUT_TYPES. Runtime widget addition - * // is forbidden per AXIOMS.md A15 / D-ban-runtime-addwidget — declare in - * // INPUT_TYPES, then bind via getWidget(). + * // Per AXIOMS.md §A1, nodes cannot enumerate widgets. To attach + * // per-widget behavior, register a widget type via `defineWidget` and + * // use the mount context's `ctx.widget` handle. This example reacts to + * // node-level execution only. * export default defineNode({ - * name: 'Comfy.PreviewAny', - * nodeTypes: ['PreviewAny'], + * name: 'my-org.executed-logger', + * nodeTypes: ['KSampler'], * * nodeCreated(node) { - * const preview = node.getWidget('preview') - * if (!preview) return * node.on('executed', (e) => { - * preview.setValue(String(e.output['text'] ?? '')) + * console.log('node executed:', e.output) * }) * } * }) @@ -183,6 +181,30 @@ export declare function defineWidget( * setup context) throws in development and silently no-ops in production. * * See {@link onMounted} for full usage examples. + * + * @example + * ```ts + * import { + * defineSidebarTab, + * onMounted, + * onUnmounted, + * onActivated, + * onDeactivated + * } from '@comfyorg/extension-api' + * + * defineSidebarTab({ + * id: 'my-tab', + * title: 'My Tab', + * type: 'vue', + * component: MyTab, + * setup() { + * onMounted(() => console.log('tab mounted')) + * onActivated(() => console.log('tab shown')) + * onDeactivated(() => console.log('tab hidden')) + * onUnmounted(() => console.log('tab unmounted')) + * } + * }) + * ``` */ export { onBeforeMount, diff --git a/src/extension-api/node.ts b/src/extension-api/node.ts index 3a490cf93e..2e9c34e895 100644 --- a/src/extension-api/node.ts +++ b/src/extension-api/node.ts @@ -29,6 +29,14 @@ export type { NodeEntityId } * **Immutable tuple.** Attempts to * mutate via `node.getPosition()[0] = X` raise a TypeScript error. Use * {@link NodeHandle.setPosition} to move the node. + * + * @example + * ```ts + * import type { Point } from '@comfyorg/extension-api' + * + * // Per-pixel mouse coordinate from a canvas event + * const cursor: Point = [event.canvasX, event.canvasY] + * ``` */ export type Point = readonly [x: number, y: number] @@ -38,6 +46,13 @@ export type Point = readonly [x: number, y: number] * **Immutable tuple.** Attempts to * mutate via `node.getSize()[0] = X` raise a TypeScript error. Use * {@link NodeHandle.setSize} to resize the node. + * + * @example + * ```ts + * import type { Size } from '@comfyorg/extension-api' + * + * const target: Size = [320, 240] + * ``` */ export type Size = readonly [width: number, height: number] @@ -368,15 +383,19 @@ export interface NodeHandle { * * // AFTER (recommended — widget-level, schema-declared) * // Declare `_my_state` in the Python node's INPUT_TYPES as a hidden - * // STRING input; the widget will exist automatically. Then attach - * // the serialization transform to the widget: - * const stateWidget = node.getWidget('_my_state')! - * stateWidget.on('beforeSerialize', (e) => { - * e.setSerializedValue(JSON.stringify(computeState())) + * // STRING input. Then attach the serialization transform inside + * // `defineWidget({mount})` — `ctx.widget` is the only legal handle: + * import { defineWidget } from '@comfyorg/extension-api' + * + * defineWidget({ + * name: 'my-org.state-shim', + * type: 'STRING', + * mount(_host, ctx) { + * ctx.widget.on('beforeSerialize', (e) => { + * e.setSerializedValue(JSON.stringify(computeState())) + * }) + * } * }) - * // Note: runtime widget addition is forbidden per AXIOMS.md A15 / - * // D-ban-runtime-addwidget — declare in INPUT_TYPES, do not call - * // node.addWidget(). * ``` * * @returns A cleanup function to remove the listener. diff --git a/src/extension-api/registrations.ts b/src/extension-api/registrations.ts index 851a737e94..7263ae4fb9 100644 --- a/src/extension-api/registrations.ts +++ b/src/extension-api/registrations.ts @@ -59,6 +59,20 @@ import type { * * @publicAPI * @stability experimental + * @example + * ```ts + * import { defineSidebarTab, type DisposableHandle } from '@comfyorg/extension-api' + * + * const handle: DisposableHandle = defineSidebarTab({ + * id: 'my-tab', + * title: 'My Tab', + * type: 'vue', + * component: MyTab + * }) + * + * // Later: tear down + * handle.dispose() + * ``` */ export interface DisposableHandle { dispose(): void diff --git a/src/extension-api/shell.ts b/src/extension-api/shell.ts index e33218c223..7e91af8448 100644 --- a/src/extension-api/shell.ts +++ b/src/extension-api/shell.ts @@ -17,14 +17,50 @@ // (ExtensionSlot.vue) import them directly from '@/types/extensionTypes'. export type { // Pre-existing types (unchanged shape) + /** + * Options bag for {@link defineSidebarTab}. + * @see {@link defineSidebarTab} for a usage example. + */ SidebarTabExtension, + /** + * Options bag for {@link defineBottomPanelTab}. + * @see {@link defineBottomPanelTab} for a usage example. + */ BottomPanelExtension, + /** + * Options bag for {@link toast.show} / {@link toast.remove}. + * @see {@link toast} for a usage example. + */ ToastMessageOptions, + /** + * Manager interface backing the {@link toast} surface. + * @see {@link toast} for a usage example. + */ ToastManager, // Net-new shell-UI arg types + /** + * Options bag for {@link defineCommand}. + * @see {@link defineCommand} for a usage example. + */ CommandDefinition, + /** + * Options bag for {@link defineHotkey}. + * @see {@link defineHotkey} for a usage example. + */ HotkeyExtension, + /** + * Options bag for {@link defineAboutBadge}. + * @see {@link defineAboutBadge} for a usage example. + */ AboutBadgeExtension, + /** + * Options bag for {@link defineSetting}. + * @see {@link defineSetting} for a usage example. + */ SettingDefinition, + /** + * Options bag for {@link defineToolbarButton}. + * @see {@link defineToolbarButton} for a usage example. + */ ToolbarButtonExtension } from '@/types/extensionTypes' diff --git a/src/extension-api/types.ts b/src/extension-api/types.ts index 2e35e0ec46..295d4935a7 100644 --- a/src/extension-api/types.ts +++ b/src/extension-api/types.ts @@ -86,12 +86,14 @@ export interface NodeExtensionOptions { * * @example * ```ts - * import { defineExtension } from '@comfyorg/extension-api' + * import { defineExtension, onMounted } from '@comfyorg/extension-api' * * export default defineExtension({ * name: 'my-org.my-extension', - * async setup() { - * // App is ready; register commands, sidebar tabs, etc. + * setup() { + * onMounted(() => { + * // App is ready; register commands, sidebar tabs, etc. + * }) * } * }) * ``` diff --git a/src/extension-api/widget.ts b/src/extension-api/widget.ts index d9a6941f5f..0df77edb53 100644 --- a/src/extension-api/widget.ts +++ b/src/extension-api/widget.ts @@ -25,6 +25,15 @@ export type { WidgetEntityId } /** * The union of all legal widget scalar values. Complex widgets (DOM, canvas) * may return their own serializable shapes. + * + * @example + * ```ts + * import type { WidgetValue } from '@comfyorg/extension-api' + * + * // `WidgetValue` is `string | number | boolean | null` — the four + * // primitive-widget value shapes. + * const val: WidgetValue = 42 + * ``` */ export type WidgetValue = string | number | boolean | null @@ -512,6 +521,20 @@ export interface WidgetHandle { * for those. * * @stability experimental + * @example + * ```ts + * import { defineWidget, type WidgetCleanup } from '@comfyorg/extension-api' + * + * defineWidget({ + * name: 'my-ext', + * type: 'STRING', + * mount(host): WidgetCleanup { + * const input = document.createElement('input') + * host.appendChild(input) + * return () => input.remove() + * } + * }) + * ``` */ export type WidgetCleanup = () => void @@ -571,6 +594,17 @@ export interface WidgetMountContext { * remount fires `ctx.onBeforeRemount` / `ctx.onAfterRemount` instead. * * @stability experimental + * @example + * ```ts + * import type { WidgetMountFn } from '@comfyorg/extension-api' + * + * const mount: WidgetMountFn = (host, ctx) => { + * const el = document.createElement('div') + * el.textContent = String(ctx.widget.getValue() ?? '') + * host.appendChild(el) + * return () => el.remove() + * } + * ``` */ export type WidgetMountFn = ( host: HTMLElement,