Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
d4ea383
feat(app-mode): layout grid primitive + system cells (Phase 1)
eliheuer Apr 16, 2026
96330d5
feat(bento): Phase 2a — per-input cells with uniform width
eliheuer Apr 14, 2026
0d31e3b
feat(bento): Phase 2b — per-output cells + design tokens
eliheuer Apr 14, 2026
43302f8
feat(app-mode): semi-customizable floating panels MVP (Phase 4)
eliheuer Apr 16, 2026
7da9659
style(app-mode): Phase 4 polish — typography, spacing, panel chrome
eliheuer Apr 16, 2026
451752b
refactor(app-mode): rename bento/ -> layout/
eliheuer Apr 16, 2026
1d16073
feat(app-mode): welcome screen — wordmark, copy, Run pill
eliheuer Apr 16, 2026
dde524f
feat(app-mode): unified chrome — shared sidebar + panel-matched cells
eliheuer Apr 16, 2026
8f8aa62
style(app-mode): flush left-dock + surface Run errors as toast
eliheuer Apr 16, 2026
a4addaa
i18n(app-mode): route output-thumb alt + block-drag aria-label throug…
eliheuer Apr 16, 2026
e28af8c
feat(app-builder): WYSIWYG floating panel shared with App Mode
eliheuer Apr 17, 2026
d9ae0dd
feat(app-mode): hoist Run cluster out of panel + mirror welcome copy
eliheuer Apr 17, 2026
c9b8e53
Merge branch 'main' into app-mode-semi-customizable-layout
DrJKL Apr 21, 2026
299292a
refactor(app-mode): address PR review feedback
eliheuer Apr 22, 2026
d436f54
fix(app-mode): second-round CodeRabbit feedback
eliheuer Apr 22, 2026
385b611
fix(app-mode): third-round CodeRabbit feedback
eliheuer Apr 22, 2026
9657403
Merge branch 'main' into app-mode-semi-customizable-layout
eliheuer Apr 22, 2026
3361a66
refactor(app-mode): unify App Mode and Builder chrome + panel
eliheuer Apr 23, 2026
3b70a54
refactor(app-mode): one grid system — fixed-gutter flex zones
eliheuer Apr 23, 2026
551a702
polish(app-builder): grid-aligned chrome bars + unified blue / orange…
eliheuer Apr 23, 2026
76dc717
polish(app-builder): highlight selectable widgets in builder:inputs
eliheuer Apr 23, 2026
550d855
polish(app-builder): output-selection parity + footer cleanup
eliheuer Apr 23, 2026
9adcea8
polish(app-mode): go/stop semantic cluster + run progress bar
eliheuer Apr 23, 2026
916ad31
refactor(app-mode): route go/stop colors through local CSS vars
eliheuer Apr 23, 2026
d621c7d
chore(app-mode): pre-squash CR-pattern cleanup
eliheuer Apr 23, 2026
69e1bff
Merge remote-tracking branch 'upstream/main' into app-mode-semi-custo…
eliheuer Apr 23, 2026
82f1980
chore(app-mode): pre-squash cleanup from .agents/checks audit
eliheuer Apr 23, 2026
fd022de
refactor(app-mode): unify App Builder and App Mode into one WYSIWYG s…
eliheuer Apr 23, 2026
e8c7076
chore: drop unused type exports flagged by knip
eliheuer Apr 23, 2026
38b197c
polish(app-mode): pre-draft-flip panel UX pass
eliheuer Apr 23, 2026
adc1db4
chore(app-mode): pre-review code-quality cleanup pass
eliheuer Apr 24, 2026
fbddb28
Merge remote-tracking branch 'upstream/main' into app-mode-semi-custo…
eliheuer Apr 24, 2026
d1f29f0
polish(app-mode): workspace pan/zoom + nav cluster + welcome touch-up
eliheuer Apr 24, 2026
32ab581
polish(app-mode): panel-block reorder — architecture + visuals
eliheuer Apr 24, 2026
583ba29
polish(app-mode): phase-3 drag demo + accent sweep
eliheuer Apr 26, 2026
0628acf
feat(app-mode): output preview as a movable window
eliheuer Apr 26, 2026
7c0d078
feat(app-mode): migrate run chrome into OutputWindow
eliheuer Apr 26, 2026
ccf166d
chore(app-mode): drop dead Interrupt/Progress cells
eliheuer Apr 26, 2026
4083442
feat(app-mode): multi-window output workspace (scaffolding)
eliheuer Apr 26, 2026
f067835
refactor(app-mode): drop the thumbnail history strip
eliheuer Apr 27, 2026
fc8e2f3
feat(app-mode): multi-window polish — asset resolution + close + clea…
eliheuer Apr 27, 2026
09d6ff6
polish(app-mode): UI pass — panel/window chrome, widget outlines, aut…
eliheuer Apr 27, 2026
8f3b9be
polish(app-mode): output overlay + widget chrome refinements
eliheuer Apr 27, 2026
d2e637b
polish(app-mode): floating-panel layout fixes + pre-review cleanup
eliheuer Apr 27, 2026
98f57a9
polish(app-mode): pan reliability + drag perf + dot-grid polish
eliheuer Apr 27, 2026
8c3fb37
polish(app-mode): address outstanding review nits
eliheuer Apr 27, 2026
a4128c8
docs(app-mode): defer agent design-system discoverability work
eliheuer Apr 27, 2026
ea0601a
chore(app-mode): comment trim pass for review (-343 lines)
eliheuer Apr 27, 2026
201fc1e
polish(app-mode): progress bar tracks sampler + window snap during drag
eliheuer Apr 27, 2026
bd4f9ef
chore(app-mode): trim small net-new files (-82 lines)
eliheuer Apr 27, 2026
17ecf38
refactor(app-mode): extract shared PanelHeader component
eliheuer Apr 27, 2026
895c0c1
refactor(app-mode): extract usePointerDrag base hook
eliheuer Apr 28, 2026
56bbd35
chore(app-mode): trim PR-added comments in modified files (-15 lines)
eliheuer Apr 28, 2026
d8355f4
chore(app-mode): drop remaining `!` Tailwind in chrome cells
eliheuer Apr 28, 2026
543f4b3
chore(app-mode): address CR review nits (batch 1/3)
eliheuer Apr 28, 2026
75b77b0
chore(app-mode): address CR review minors (batch 2/3)
eliheuer Apr 28, 2026
d676184
fix(app-mode): address CR review majors (batch 3/3)
eliheuer Apr 28, 2026
ed93de5
docs(app-mode): drop stale check count in discoverability note
eliheuer Apr 28, 2026
36c23a6
test(app-mode): fix LinearWelcome tests broken by run-pill addition
eliheuer Apr 28, 2026
ff60b57
polish(builder): step badges + Preview-step layout
eliheuer Apr 28, 2026
1289e61
refactor(app-mode): drop temp accent/active tokens for design-system
eliheuer Apr 28, 2026
d4dc570
refactor(builder): unify selection chrome, drop zoom-dependent sizing
eliheuer Apr 28, 2026
f6a29be
refactor(design-system): fold layout tokens into style.css
eliheuer Apr 28, 2026
c497c8c
refactor(design-system): consolidate App Mode tokens, drop README
eliheuer Apr 28, 2026
90f140b
chore(app-mode): trim noisy comments across new files
eliheuer Apr 28, 2026
563742f
feat(app-mode): resize OutputWindow, smooth fly-to, grid-edge spawn
eliheuer Apr 29, 2026
1d16598
feat(app-mode): align panel widgets + metadata-driven subtitle
eliheuer Apr 29, 2026
61ff478
refactor(app-mode): extract FLIP composable, persist panel layout
eliheuer Apr 29, 2026
d434a0d
chore(app-mode): comment sweep + LinearArrange refactor
eliheuer Apr 29, 2026
91bd183
refactor(app-mode): center welcome card; drop side-flip machinery
eliheuer Apr 29, 2026
b007a55
fix(app-mode): drop double focus-ring on widget pills
eliheuer Apr 29, 2026
676a443
Merge branch 'main' into app-mode-semi-customizable-layout
DrJKL Apr 29, 2026
13b3e09
feat(app-mode): no-zoom dashboard mode with adaptive bento layout
eliheuer Apr 30, 2026
6f91765
feat(app-mode): add slicing-tree bento candidate
eliheuer Apr 30, 2026
2a21d12
fix(app-mode): snap dashboard tiles to chrome cell grid
eliheuer Apr 30, 2026
c274133
feat(app-mode): fill canvas via slicing-tree + stretch + cover-fit
eliheuer Apr 30, 2026
0d18fe0
feat(app-mode): asymmetric bento stretch — newest tile uncropped
eliheuer May 1, 2026
8f00acb
feat(app-mode): cap dashboard at 9 tiles, full slicing-tree always
eliheuer May 1, 2026
5edcdf8
fix(app-mode): drop the 8px body padding around output images
eliheuer May 1, 2026
274d32c
fix(app-mode): N=1 stays uncropped, anchored opposite the panel
eliheuer May 1, 2026
152653a
refactor(app-mode): rebuild dashboard layout for visual fidelity
eliheuer May 1, 2026
1f0cfea
feat(app-mode): bento templates fill avail rect, hide header on small…
eliheuer May 1, 2026
de52814
refactor(app-mode): clean up output header chrome + fix panel-drag fr…
eliheuer May 1, 2026
4754980
style(app-mode): match input-cell label/subtitle type, breathe out te…
eliheuer May 1, 2026
681a959
feat(app-mode): theme toggle, zoom-mode parity, gradient skeletons
eliheuer May 2, 2026
989754e
Merge remote-tracking branch 'upstream/main' into app-mode-semi-custo…
eliheuer May 6, 2026
4fb60b8
refactor(app-mode): use palette tokens instead of local --app-mode-* …
eliheuer May 6, 2026
2746856
refactor(app-mode): tighten design-system conformance
eliheuer May 6, 2026
9c171c1
refactor(app-mode): conformance pass per AGENTS.md
eliheuer May 6, 2026
055e46e
refactor(app-mode): minor cleanups from deep audit
eliheuer May 7, 2026
2640d09
lint(autofix): tailwind class order in files using foreground tokens
eliheuer May 7, 2026
c6cf567
test(app-mode): include layout.panelRows in linearData expectations
eliheuer May 7, 2026
fd75632
test(app-mode): unit coverage for new store math
eliheuer May 7, 2026
1acdb42
refactor(app-mode): scope foreground tokens to App Mode via arbitrary…
eliheuer May 7, 2026
a4d4145
Revert "lint(autofix): tailwind class order in files using foreground…
eliheuer May 7, 2026
1a7300b
lint(autofix): canonical class names in pre-existing files
eliheuer May 7, 2026
7e5a95a
fix(app-mode): evict by createdSeq, not mutable zIndex
eliheuer May 7, 2026
6b9dfe8
fix(test): pass ComfyPage to reSaveAs instead of dereferencing Locator
eliheuer May 7, 2026
f357c57
fix(app-mode): spread graphNodes shallowRef on every assignment
eliheuer May 7, 2026
c668618
fix(app-mode): disable manual drag/resize on output windows in noZoom…
eliheuer May 7, 2026
4c140cb
fix(app-mode): don't silently rerun current graph when output has no …
eliheuer May 7, 2026
1a1025b
fix(app-mode): scope wheel-zoom to the workspace, leave panel scroll …
eliheuer May 7, 2026
c48d731
fix(app-mode): keep full-viewport backdrop hit-testable for pan/zoom
eliheuer May 7, 2026
5a31405
fix(app-mode): address 5 minor CodeRabbit findings
eliheuer May 7, 2026
2f7e90a
fix(app-mode): address 5 load-bearing CodeRabbit findings (round 2)
eliheuer May 8, 2026
7a5e375
docs(adr): add 0009 App Mode + Builder unification; format pass
eliheuer May 9, 2026
c997195
fix(app-mode): address round-3 CodeRabbit findings (5 follow-ons + 4 …
eliheuer May 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(app-mode): unified chrome — shared sidebar + panel-matched cells
- Share SideToolbar with Graph mode (App Mode previously had a
  custom subset)
- Panel-matched chrome across mode toggle, builder, share,
  feedback, and action cells; real Run pill in welcome
- Decorative dot grid on the canvas background
- Simplify info cell; round output thumbnails
  • Loading branch information
eliheuer committed Apr 16, 2026
commit dde524f8fe9a8befb7da6b615f09b0b143ca16b8
122 changes: 60 additions & 62 deletions src/components/appMode/layout/LayoutView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import { useAppModeStore } from '@/stores/appModeStore'
import { useCommandStore } from '@/stores/commandStore'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { useFeatureFlags } from '@/composables/useFeatureFlags'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import { isCloud } from '@/platform/distribution/types'
import {
openShareDialog,
Expand All @@ -65,26 +64,12 @@ const appModeStore = useAppModeStore()
const { enterBuilder } = appModeStore
const { hasNodes } = storeToRefs(appModeStore)
const commandStore = useCommandStore()
const workspaceStore = useWorkspaceStore()
const workflowStore = useWorkflowStore()
const { toastErrorHandler } = useErrorHandling()
const { flags } = useFeatureFlags()

const isAssetsActive = computed(
() => workspaceStore.sidebarTab.activeSidebarTab?.id === 'assets'
)
const isAppsActive = computed(
() => workspaceStore.sidebarTab.activeSidebarTab?.id === 'apps'
)

const showShare = computed(() => isCloud && flags.workflowSharingEnabled)

function openAssets() {
void commandStore.execute('Workspace.ToggleSidebarTab.assets')
}
function showApps() {
void commandStore.execute('Workspace.ToggleSidebarTab.apps')
}
function openShare() {
openShareDialog().catch(toastErrorHandler)
}
Expand Down Expand Up @@ -384,18 +369,6 @@ function actionDownload() {
if (url) downloadFile(url)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/** Middle-truncate a filename like a hash: `first…last`. Preserves the
* extension when present. 4/4 chars each side matches what the user
* asked for ("1234…5678"). */
function truncateMiddle(name: string, head = 4, tail = 4): string {
if (!name) return ''
const dot = name.lastIndexOf('.')
const ext = dot > 0 && dot > name.length - 6 ? name.slice(dot) : ''
const base = ext ? name.slice(0, dot) : name
if (base.length <= head + tail + 1) return name
return `${base.slice(0, head)}…${base.slice(-tail)}${ext}`
}

const infoName = computed(() => {
const sel = selectedHistory.value
if (!sel) return ''
Expand Down Expand Up @@ -425,7 +398,12 @@ watch(
const infoDims = computed(() =>
dimensions.value ? `${dimensions.value.w}x${dimensions.value.h}` : ''
)
const infoLabel = computed(() => truncateMiddle(infoName.value))
// Show only the file extension in the cell (e.g. "png"); full filename
// stays in the tooltip/aria-label so nothing is lost.
const infoLabel = computed(() => {
const dotIdx = infoName.value.lastIndexOf('.')
return dotIdx >= 0 ? infoName.value.slice(dotIdx + 1).toLowerCase() : ''
})
const infoTitle = computed(() =>
[infoDims.value, infoName.value].filter(Boolean).join(' ')
)
Expand Down Expand Up @@ -461,45 +439,52 @@ const historyThumbMap = computed(
const cells = computed<LayoutCellPlacement[]>(() => {
const out: LayoutCellPlacement[] = []

let row = 1
if (enableAppBuilder.value) {
out.push({ id: 'builder', col: 1, row: row++, kind: 'system-builder' })
}
if (showShare.value) {
out.push({ id: 'share', col: 1, row: row++, kind: 'system-share' })
}
out.push({ id: 'assets', col: 1, row: row++, kind: 'system-assets' })
out.push({ id: 'apps', col: 1, row: row++, kind: 'system-apps' })

// Row 1, left-to-right: App/Graph mode toggle, then optional builder +
// optional share. Toggle is anchored at col 1 so it doesn't shift when
// builder / share visibility changes; builder + share slot in to its
// right.
out.push({
id: 'mode-toggle',
col: 2,
col: 1,
row: 1,
colSpan: 2,
kind: 'system-mode-toggle'
})
let col = 3
if (enableAppBuilder.value) {
out.push({ id: 'builder', col: col++, row: 1, kind: 'system-builder' })
}
if (showShare.value) {
out.push({ id: 'share', col: col++, row: 1, kind: 'system-share' })
}

// Action cells on row 1, right of the mode toggle. Only mount when
// Action cells on row 1, right of builder/share. Only mount when
// a history item is selected (mirrors LinearPreview's top bar).
if (hasSelection.value) {
out.push({ id: 'action-rerun', col: 4, row: 1, kind: 'action-rerun' })
const actionStartCol = col
out.push({
id: 'action-rerun',
col: actionStartCol,
row: 1,
kind: 'action-rerun'
})
out.push({
id: 'action-reuse-params',
col: 5,
col: actionStartCol + 1,
row: 1,
kind: 'action-reuse-params'
})
out.push({
id: 'action-download',
col: 6,
col: actionStartCol + 2,
row: 1,
kind: 'action-download'
})
out.push({
id: 'action-info',
col: 7,
col: actionStartCol + 3,
row: 1,
colSpan: 5,
colSpan: 3,
kind: 'action-info'
})
}
Expand All @@ -512,9 +497,8 @@ const cells = computed<LayoutCellPlacement[]>(() => {
kind: 'system-feedback'
})

// History thumbs: one per row, stacking down column 1 immediately
// below the icon stack (no gap).
const thumbStartRow = row
// History thumbs stack down column 1 starting below the chrome row.
const thumbStartRow = 2
for (let i = 0; i < historyThumbs.value.length; i++) {
out.push({
id: historyThumbs.value[i].id,
Expand Down Expand Up @@ -554,20 +538,6 @@ const cells = computed<LayoutCellPlacement[]>(() => {
:on-activate="openShare"
@pointerenter="prefetchShareDialog"
/>
<IconCell
v-else-if="cell.kind === 'system-assets'"
icon="icon-[comfy--image-ai-edit]"
:label="t('sideToolbar.mediaAssets.title')"
:active="isAssetsActive"
:on-activate="openAssets"
/>
<IconCell
v-else-if="cell.kind === 'system-apps'"
icon="icon-[lucide--panels-top-left]"
:label="t('linearMode.appModeToolbar.apps')"
:active="isAppsActive"
:on-activate="showApps"
/>
<ModeToggleCell v-else-if="cell.kind === 'system-mode-toggle'" />
<IconCell
v-else-if="cell.kind === 'action-rerun'"
Expand Down Expand Up @@ -639,6 +609,16 @@ const cells = computed<LayoutCellPlacement[]>(() => {
position: absolute;
inset: 0;
background-color: var(--layout-color-canvas);
/* Form-builder dot grid — decorative. Not aligned with LayoutGrid
cell positions (the grid's gaps expand to absorb viewport slack,
so cell corners drift relative to any fixed-pitch pattern). */
background-image: radial-gradient(
circle,
var(--layout-color-grid-dot) 1px,
transparent 1.5px
);
background-size: var(--layout-dot-grid-size) var(--layout-dot-grid-size);
background-position: 0 0;
/* Clip LinearPreview's inner absolute-positioned layers (image,
skeletons, welcome) so they can never paint above the layout-view
box — which sits below the top workflow-tabs bar. */
Expand Down Expand Up @@ -673,6 +653,24 @@ const cells = computed<LayoutCellPlacement[]>(() => {
pointer-events: auto;
}

/* Panel-matched chrome: every system + action cell borrows
FloatingPanel's surface styling (1px hairline border + 10px
radius) so the whole App Mode chrome reads as one family. */
.layout-view :deep(.layout-cell[data-cell-kind='system-mode-toggle']),
.layout-view :deep(.layout-cell[data-cell-kind='system-builder']),
.layout-view :deep(.layout-cell[data-cell-kind='system-share']),
.layout-view :deep(.layout-cell[data-cell-kind='system-feedback']),
.layout-view :deep(.layout-cell[data-cell-kind='action-rerun']),
.layout-view :deep(.layout-cell[data-cell-kind='action-reuse-params']),
.layout-view :deep(.layout-cell[data-cell-kind='action-download']),
.layout-view :deep(.layout-cell[data-cell-kind='action-info']),
.layout-view :deep(.layout-cell[data-cell-kind='output-thumb']) {
box-sizing: border-box;
border: 1px solid rgb(255 255 255 / 0.08);
border-radius: 10px;
overflow: hidden;
}

.info-cell {
display: flex;
width: 100%;
Expand Down
10 changes: 9 additions & 1 deletion src/components/appMode/layout/cells/FeedbackCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const { t } = useI18n()
display: flex;
flex-direction: column;
font-size: var(--layout-font-md);
line-height: 1.3;
line-height: 1.1;
color: var(--layout-color-text-muted);
white-space: nowrap;
overflow: hidden;
Expand All @@ -65,4 +65,12 @@ const { t } = useI18n()
background-color: var(--layout-color-cell-hover) !important;
color: var(--layout-color-text) !important;
}

/* Match the 20px icon size of the Builder / Share IconCells so all
chrome icons read at the same scale. */
.feedback-cell :deep(button i),
.feedback-cell :deep(a i) {
width: 20px;
height: 20px;
}
</style>
8 changes: 8 additions & 0 deletions src/components/appMode/layout/cells/ModeToggleCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,12 @@ import WorkflowActionsDropdown from '@/components/common/WorkflowActionsDropdown
width: 100%;
padding: 0 !important;
}

/* Match the 20px icon size of the Builder / Share / Feedback chrome.
Targets the leading App/Graph icon (m-1 is the icon-button wrapper)
without touching the dropdown chevron. */
.mode-toggle-cell :deep(button.m-1 > i) {
width: 20px;
height: 20px;
}
</style>
8 changes: 8 additions & 0 deletions src/components/appMode/layout/design-tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
--layout-gutter-min: 8px; /* minimum gap between cells */
--layout-outer-padding: 16px; /* margin from viewport edge */
--layout-cell-radius: 4px; /* cell background corner radius */
--layout-dot-grid-size: 24px; /* pitch of the canvas dot grid (decorative) */

/* --- Colors ---------------------------------------------------------
Neutral palette follows Comfy's existing tokens. Accent is used
Expand All @@ -40,6 +41,13 @@
--layout-color-accent-hover: #2c974b;
--layout-color-accent-active: #298e46;
--layout-color-accent-foreground: #ffffff;
/* Form-builder-style dot pattern on the canvas. Muted text color at
low alpha so it reads as texture, not content. */
--layout-color-grid-dot: color-mix(
in srgb,
var(--layout-color-text-muted) 35%,
transparent
);

/* --- Motion --------------------------------------------------------- */
--layout-transition-duration: 150ms;
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/extensions/linearMode/LinearWelcome.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
})
}))

vi.mock('@/stores/commandStore', () => ({
useCommandStore: () => ({
execute: vi.fn()
})
}))

const i18n = createI18n({ legacy: false, locale: 'en', missingWarn: false })

function renderComponent(
Expand Down
24 changes: 21 additions & 3 deletions src/renderer/extensions/linearMode/LinearWelcome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemp
import { useAppModeStore } from '@/stores/appModeStore'
import Button from '@/components/ui/button/Button.vue'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
import { useCommandStore } from '@/stores/commandStore'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
import AppModeWordmark from './AppModeWordmark.vue'
Expand All @@ -18,6 +19,20 @@ const isAppDefault = computed(
() => workflowStore.activeWorkflow?.initialMode === 'app'
)
const templateSelectorDialog = useWorkflowTemplateSelectorDialog()

// Same command the corner RunCell dispatches. Shift-click queues to
// the front of the line; plain click appends.
const commandStore = useCommandStore()
async function runFromPill(e: MouseEvent) {
const commandId = e.shiftKey ? 'Comfy.QueuePromptFront' : 'Comfy.QueuePrompt'
try {
await commandStore.execute(commandId, {
metadata: { subscribe_to_run: false, trigger_source: 'linear' }
})
} catch (error) {
console.error('[LinearWelcome] Queue prompt failed:', error)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
</script>

<template>
Expand Down Expand Up @@ -47,15 +62,18 @@ const templateSelectorDialog = useWorkflowTemplateSelectorDialog()
<p class="mt-0 text-base-foreground">
<i18n-t keypath="linearMode.welcome.getStarted" tag="span">
<template #runButton>
<span
class="mx-1 inline-flex -translate-y-0.5 transform cursor-default items-center rounded-sm px-3 py-1 text-base font-medium"
<button
type="button"
class="mx-1 inline-flex translate-y-px transform cursor-pointer items-center gap-2 rounded-md border-0 px-4 py-1.5 text-xl font-medium outline-none focus:outline-none"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
:style="{
backgroundColor: 'var(--layout-color-accent)',
color: 'var(--layout-color-accent-foreground)'
}"
@click="runFromPill"
>
<i class="icon-[lucide--play] size-5" />
{{ t('menu.run') }}
</span>
</button>
</template>
</i18n-t>
</p>
Expand Down
30 changes: 28 additions & 2 deletions src/views/LinearView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import AppModeToolbar from '@/components/appMode/AppModeToolbar.vue'
import LayoutView from '@/components/appMode/layout/LayoutView.vue'
import ExtensionSlot from '@/components/common/ExtensionSlot.vue'
import ErrorOverlay from '@/components/error/ErrorOverlay.vue'
import SideToolbar from '@/components/sidebar/SideToolbar.vue'
import TopbarBadges from '@/components/topbar/TopbarBadges.vue'
import TopbarSubscribeButton from '@/components/topbar/TopbarSubscribeButton.vue'
import WorkflowTabs from '@/components/topbar/WorkflowTabs.vue'
Expand Down Expand Up @@ -119,9 +120,34 @@ function dragDrop(e: DragEvent) {
</div>
<div
v-if="showLayoutView"
class="relative h-[calc(100%-var(--workflow-tabs-height))] w-full"
class="flex h-[calc(100%-var(--workflow-tabs-height))] w-full"
>
<LayoutView />
<SideToolbar />
<Splitter
v-if="activeTab"
class="flex-1 border-none bg-transparent"
state-key="Comfy.LinearView.SidebarPanelSplitter"
state-storage="local"
@resizestart="$event.originalEvent.preventDefault()"
Comment on lines +124 to +129
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and examine the target lines
if [ -f "src/views/LinearView.vue" ]; then
  echo "File exists. Examining lines 120-135:"
  sed -n '120,135p' src/views/LinearView.vue
else
  echo "File not found"
  find . -name "LinearView.vue" -type f
fi

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 699


🏁 Script executed:

# Get more context around the splitter to understand the panel structure
sed -n '100,170p' src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2359


🏁 Script executed:

# Search for sidebarOnLeft usage in the file
grep -n "sidebarOnLeft" src/views/LinearView.vue | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 936


🏁 Script executed:

# Check how SplitterPanel elements are arranged and ordered
grep -n "SplitterPanel\|Splitter" src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 871


🏁 Script executed:

# Find and examine the useStablePrimeVueSplitterSizer composable
find . -name "*useStablePrimeVueSplitterSizer*" -o -name "*SplitterSizer*" | head -10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 179


🏁 Script executed:

# Search for the composable definition
rg -l "useStablePrimeVueSplitterSizer" --type ts --type js

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 200


🏁 Script executed:

# Check the composable content
find . -path "*/composables/*" -name "*.ts" | xargs grep -l "useStablePrimeVueSplitterSizer" 2>/dev/null

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 179


🏁 Script executed:

# Examine the useStablePrimeVueSplitterSizer composable
cat -n src/composables/useStablePrimeVueSplitterSizer.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2321


🏁 Script executed:

# Also check the test file to understand the expected behavior
cat -n src/composables/useStablePrimeVueSplitterSizer.test.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 4553


🏁 Script executed:

# Check how useStablePrimeVueSplitterSizer is called in LinearView.vue
sed -n '80,95p' src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 604


🏁 Script executed:

# Find the panel refs that are passed to the composable
grep -n "const.*Ref\|template.*ref\=" src/views/LinearView.vue | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 438


🏁 Script executed:

# Get a broader view of the composable usage and panel refs setup
sed -n '1,100p' src/views/LinearView.vue | tail -50

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1889


🏁 Script executed:

# Check if the inner Splitter (lines 124-163) uses the composable at all
# Search for refs or state-key handling in that section
sed -n '124,163p' src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1390


🏁 Script executed:

# Check if there are any template refs for the inner splitter panels
grep -n "ref=\".*Panel\|#.*Panel" src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 127


🏁 Script executed:

# Get the complete section including all splitters and their configurations
sed -n '120,200p' src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2708


🏁 Script executed:

# Check if there's any other state management or key handling for the inner splitter
grep -B5 -A15 "state-key=\"Comfy.LinearView.SidebarPanelSplitter\"" src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 814


🏁 Script executed:

# Verify the panel structure and conditional rendering more carefully
sed -n '124,163p' src/views/LinearView.vue | grep -n "SplitterPanel\|v-if"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 328


Key the persisted splitter state by sidebar side.

sidebarOnLeft swaps the panel order in the Splitter, but state-key remains constant. PrimeVue restores splitter sizes by panel index, so a saved sidebar width can be reapplied to the wrong panel after the side flips. Make the state-key side-specific so restored widths always belong to the correct panel.

Proposed fix
-        state-key="Comfy.LinearView.SidebarPanelSplitter"
+        :state-key="
+          `Comfy.LinearView.SidebarPanelSplitter.${
+            sidebarOnLeft ? 'left' : 'right'
+          }`
+        "
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/views/LinearView.vue` around lines 124 - 129, The Splitter's persisted
state-key is constant so when sidebarOnLeft toggles the panel order the saved
sizes get applied to the wrong panel; update the Splitter usage to include
sidebar side information in the state-key (e.g., append or interpolate
sidebarOnLeft into the state-key string) so the key is unique per side, ensuring
the Splitter component's state-key reflects sidebarOnLeft and thus restores
widths to the correct panel.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred — LinearView's sidebar handling at this line interacts with several already-sidebar-aware computeds (showRightBuilder, showLeftBuilder, etc.). The remaining edge case is small enough to address alongside the broader sidebar-location pass that would also clean up LinearArrange.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description images show:

  • Image 1 (App view with cyberpunk eye): A grid workspace with two image cards, a floating “Output” progress window (step 13/30, 32s), and a right properties panel for node image_anima_preview--005 with prompt, width/height and filename_prefix controls.
  • Image 2 (Grid gallery “green-eyes-001”): Many eye images in a masonry grid; right panel shows prompt, Number of runs, Run, and size controls.
  • Image 3 (“anima-bunny-001”): Multiple bunny outputs; a small floating progress overlay (step 22/30, 14s); right panel with prompt and size fields.
  • Image 4 (Large gallery, tall aspect): Dense grid of eyes; floating “Output” window showing step 20/20; right panel (width 512, height 768).
  • Image 5 (“bunny-anima-001”, 1024x1024): Large selected image left, variations middle, and an “Input” panel right with a 1024x1024 prompt.

@eliheuer Thanks—acknowledging the deferral. Bundling this with the broader Comfy.Sidebar.Location pass (and the LinearArrange cleanup) makes sense and aligns with the stored guideline we have about respecting the sidebar location across views.

For that future pass, here’s the tiny change we’ll queue up to avoid cross‑applying saved widths when the sidebar flips in LinearView (around Line 124 where the Splitter is created in the showLayoutView branch):

<Splitter
  v-if="activeTab"
  class="flex-1 border-none bg-transparent"
  :state-key="`Comfy.LinearView.SidebarPanelSplitter.${sidebarOnLeft ? 'left' : 'right'}`"
  state-storage="local"
  `@resizestart`="$event.originalEvent.preventDefault()"
>

Proposed acceptance criteria for the follow-up:

  • Toggling Comfy.Sidebar.Location left/right preserves the last width for each side independently when returning to LinearView with an active tab.
  • Border placement and panel order already handled by existing computeds remain correct.
  • No changes to the builder Splitter behavior or stored widths.

Light test plan (later):

  • Manual: set widths with sidebar on left, reload; flip to right, set different width, reload; flip back—verify each side restores its own width.
  • Optional Playwright: assert localStorage keys diverge by suffix (.left/.right) and sizes restore after reload.

If you’d like, I can open a small tracking issue for this and reference this PR/comment, assigning it to you. Should I go ahead and create that GitHub issue now?


🧠 Learnings used
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In components that use the cn utility from '@/utils/tailwindUtil' with tailwind-merge, rely on the behavior that conflicting Tailwind classes are resolved by keeping the last one. For example, cn('base-classes bg-default', propClass) will have any conflicting background class from propClass override bg-default. This additive pattern is intentional and aligns with the shadcn-ui convention; ensure you document or review expectations accordingly in Vue components.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8195
File: src/platform/assets/components/MediaAssetFilterBar.vue:16-16
Timestamp: 2026-01-21T01:28:27.626Z
Learning: In Vue templates (Vue 3.4+ with the build step), when binding to data or props that are camelCase (e.g., mediaTypeFilters), you can use kebab-case in the template bindings (e.g., :media-type-filters). This is acceptable and will resolve to the corresponding camelCase variable. Do not require CamelCase in template bindings.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8090
File: src/platform/assets/components/modelInfo/ModelInfoField.vue:8-11
Timestamp: 2026-01-22T02:28:58.105Z
Learning: In Vue 3 script setup, props defined with defineProps are automatically available by name in the template without destructuring. Destructuring the result of defineProps inside script can break reactivity; prefer accessing props by name in the template. If you need to use props in the script, reference them via the defined props object rather than destructuring, or use toRefs when you intend to destructure while preserving reactivity.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8497
File: src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue:223-236
Timestamp: 2026-02-01T21:10:29.567Z
Learning: In Vue single-file components, do not review or require edits to comments. Favor self-documenting code through clear naming, strong types, and explicit APIs. If a comment is misleading or outdated, consider removing it, but avoid suggesting adding or fixing comments. This guideline aligns with preferring code clarity over comment maintenance across all .vue files.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8592
File: src/components/topbar/WorkflowExecutionIndicator.vue:28-28
Timestamp: 2026-02-03T21:35:40.889Z
Learning: In Vue single-file components where the i18n t function is only used within the template, prefer using the built-in $t in the template instead of importing useI18n and destructuring t in the script. This avoids unnecessary imports when t is not used in the script. If you need i18n in the script (Composition API), only then use useI18n and access t from its returned object. Ensure this pattern applies to all Vue components with template-only i18n usage.

Learnt from: Yourz
Repo: Comfy-Org/ComfyUI_frontend PR: 8548
File: src/components/common/TreeExplorerV2Node.vue:9-47
Timestamp: 2026-02-07T14:47:06.751Z
Learning: In all Vue files that render Reka UI context menu components (ContextMenuRoot, ContextMenuTrigger, etc.), avoid dynamic component wrapping with <component :is="...">. Use conditional rendering with v-if / v-else to render separate branches, even if it results in some template duplication. This improves readability and maintainability across files that use these components.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8753
File: src/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue:17-28
Timestamp: 2026-02-09T03:24:47.113Z
Learning: When destructuring reactive properties from Pinia stores in Vue components, use storeToRefs() to preserve reactivity. Example: const store = useCanvasStore(); const { canvas } = storeToRefs(store); access as canvas.value (e.g., canvas.value). Ensure you import storeToRefs from 'pinia' and use it wherever you destructure store properties in the setup function.

Learnt from: pythongosssss
Repo: Comfy-Org/ComfyUI_frontend PR: 8775
File: src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue:62-68
Timestamp: 2026-02-10T17:59:25.893Z
Learning: In Vue 3 single-file components, code defined outside the script setup scope cannot access variables or helpers defined inside setup. If a default function (e.g., defineModel) is hoisted outside script setup, it will not be able to call setup helpers or reference setup-scoped variables. Place such logic inside setup or expose necessary values via returns/defineExpose to be accessible.

Learnt from: Yourz
Repo: Comfy-Org/ComfyUI_frontend PR: 9085
File: src/components/sidebar/tabs/NodeLibrarySidebarTabV2.vue:45-50
Timestamp: 2026-02-27T09:39:47.145Z
Learning: In Vue components that use Reka UI primitives such as DropdownMenuTrigger and SelectTrigger with as-child, it is acceptable to render a raw <button> when the styling is context-specific and there is no existing Button variant to map to. The as-child pattern preserves accessibility while enabling composition. Do not create single-use Button variants for these cases; prefer inline styling with a raw button for clarity and to avoid unnecessary abstraction. This guideline applies to all Vue components (files ending in .vue) in projects using Reka UI.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 9427
File: src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenuFilter.vue:33-33
Timestamp: 2026-03-06T00:53:28.835Z
Learning: When reviewing code, note that the enforce-canonical-classes (better-tailwindcss) rule may auto-fix Tailwind v3 !class-name syntax by converting it to v4 class-name! syntax. Do not treat these auto-fixed class-name! instances as newly introduced issues; the perceived change is in syntax placement, not in usage or intent. This guidance applies across all .vue and .ts files in the repository.

Learnt from: sonnybox
Repo: Comfy-Org/ComfyUI_frontend PR: 9446
File: src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.vue:45-45
Timestamp: 2026-03-06T01:55:00.013Z
Learning: Treat wrap-break-word as a valid Tailwind CSS utility for overflow-wrap: break-word in Tailwind v4+ projects. Do not flag this class as invalid in any Vue (.vue) or TypeScript (.ts/.tsx) files within the repository (e.g., Comfy-Org/ComfyUI_frontend) or other Tailwind v4+ projects. When reviewing, verify that the class is used to enable word breaking in long text content and reference the Tailwind docs: https://tailwindcss.com/docs/overflow-wrap.

Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 9749
File: src/platform/assets/components/MediaAssetContextMenu.vue:88-93
Timestamp: 2026-03-11T19:12:43.621Z
Learning: For Vue components using PrimeVue ContextMenu, the root element merges all passthrough attributes via v-bind="ptmi('root')". Passing an id through :pt="{ root: { id: someId } }" will reliably set the root element's id, allowing document.getElementById(someId) to reference the overlay. Do not flag this pattern as unreliable in reviews for components using PrimeVue ContextMenu with pt.root.id passthrough (e.g., src/platform/assets/components/MediaAssetContextMenu.vue).

Learnt from: Yourz
Repo: Comfy-Org/ComfyUI_frontend PR: 9965
File: src/platform/cloud/onboarding/CloudSubscriptionRedirectView.vue:77-79
Timestamp: 2026-03-15T15:24:17.291Z
Learning: Maintain narrowly scoped PRs by limiting changes to the stated fix within a single PR. Do not bundle cross-file refactors (e.g., adding concurrency guards across composables like useBillingContext) into a targeted bug-fix PR; instead propose these as separate follow-up issues or PRs. This guidance applies to Vue components (e.g., CloudSubscriptionRedirectView.vue) and other files across the repository. When a change touches multiple files or concerns broader architecture, split into separate, well-scoped PRs to improve reviewability and risk assessment.

Learnt from: eliheuer
Repo: Comfy-Org/ComfyUI_frontend PR: 11317
File: src/components/appMode/layout/cells/FeedbackCell.vue:0-0
Timestamp: 2026-04-28T07:02:16.613Z
Learning: In Vue SFCs (files under src/**/*.vue), avoid using Tailwind “important” modifiers (`!`, e.g., `!bg-*`, `!text-*`). For overrides that are truly needed, prefer defining/overriding styles in the component’s scoped <style> block using Vue’s `:deep()` selectors instead of relying on Tailwind `!` (e.g., follow the approach used in FeedbackCell.vue and ModeToggleCell.vue).

Learnt from: eliheuer
Repo: Comfy-Org/ComfyUI_frontend PR: 11317
File: src/platform/cloud/subscription/components/PricingTable.vue:0-0
Timestamp: 2026-05-07T11:23:16.180Z
Learning: In ComfyUI_frontend Vue components, do not use the Tailwind foreground alias utility classes like `text-success-foreground` (they are intentionally descoped/unknown to Tailwind in App Mode). Instead, use arbitrary-value CSS tokens such as `text-(--success-foreground)` (or an explicit semantic token like `text-(--success-background)` when the context calls for background vs foreground). During review, flag suggestions that use `text-*-foreground` aliases and prefer `text-(--<token>)` forms.

Learnt from: eliheuer
Repo: Comfy-Org/ComfyUI_frontend PR: 11317
File: src/views/LinearView.vue:124-163
Timestamp: 2026-05-08T23:31:33.868Z
Learning: In Comfy-Org/ComfyUI_frontend, any Vue view that renders a sidebar/extension panel must respect the Comfy.Sidebar.Location setting. When adding/branching layout logic (e.g., LinearView’s showLayoutView path), conditionally (1) reorder the Splitter panel(s) and (2) flip the sidebar border styling (e.g., border-r/border-l) so the sidebar/extension appears on the configured side. Ensure this behavior is implemented consistently across all views in src/views/**/*.vue that render such panels.

Learnt from: eliheuer
Repo: Comfy-Org/ComfyUI_frontend PR: 11317
File: src/components/builder/BuilderToolbar.vue:59-77
Timestamp: 2026-05-08T23:31:37.825Z
Learning: If the app does not support runtime locale switching mid-session, avoid reactive i18n updates (e.g., don’t set up locale-dependent reactive/computed/watch-based translations). In Vue components, it’s acceptable to call i18n’s `t()` during `setup` and cache the resulting strings (such as UI/step titles), so the UI remains consistent without attempting reactive locale changes until locale switching is actually implemented.

Learnt from: eliheuer
Repo: Comfy-Org/ComfyUI_frontend PR: 11317
File: src/components/appMode/layout/cells/InputCell.vue:95-101
Timestamp: 2026-05-08T23:32:07.123Z
Learning: In Comfy-Org/ComfyUI_frontend, treat the Tailwind-like responsive variant prefix `not-md:` as an intentional, repo-established convention. During code review, do not suggest replacing `not-md:` with `max-md:` (e.g., for responsive sizing used by NodeWidgets); consider `not-md:` valid wherever class names are defined in `src/**/*.vue` and top-level `*.ts` files.

Learnt from: eliheuer
Repo: Comfy-Org/ComfyUI_frontend PR: 11317
File: src/components/appMode/layout/panels/PanelBlockList.vue:143-147
Timestamp: 2026-05-08T23:35:15.715Z
Learning: In the ComfyUI frontend (Vue components), it’s acceptable to use the CSS `field-sizing: content` on `<textarea>` elements. Rely on the native fallback behavior in browsers that don’t support it (i.e., no auto-sizing) and do not add a JavaScript polyfill for this feature unless product requirements explicitly change to require auto-sizing across unsupported browsers.

>
<SplitterPanel
:size="SIDE_PANEL_SIZE"
:min-size="SIDEBAR_MIN_SIZE"
class="min-w-78 overflow-hidden"
>
<div
class="size-full overflow-x-hidden border-r border-border-subtle"
>
<ExtensionSlot :extension="activeTab" />
</div>
</SplitterPanel>
<SplitterPanel :size="100 - SIDE_PANEL_SIZE" class="relative">
<LayoutView />
</SplitterPanel>
</Splitter>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<div v-else class="relative flex-1">
<LayoutView />
</div>
</div>
Comment on lines +119 to +168
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find any drop / drag-drop handling inside the new LayoutView + App Mode tree
fd -e vue . src/components/appMode | xargs rg -n -C2 -P '@drop|ondrop|handleDragDrop|\bdrop\s*\(|DropZone'
rg -n -C2 -P 'handleDragDrop' src/renderer/extensions/linearMode

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2902


🏁 Script executed:

# Find LayoutView component definition
fd -type f -name "LayoutView.vue" src/

# Check what DropZone does - is it for workflows or inputs?
cat -n src/renderer/extensions/linearMode/DropZone.vue | head -100

# Check if LayoutView has any drop/drag handlers
rg -n "@drop|ondrop|@dragover|dragDrop" src/views/LinearView.vue

# Check if LinearControls is still mounted anywhere in the showLayoutView context
rg -n "LinearControls" src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 4035


🏁 Script executed:

# Get a clearer view of LinearView.vue structure around the new showLayoutView branch
cat -n src/views/LinearView.vue | sed -n '100,250p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 6075


🏁 Script executed:

# Find LayoutView component definition to check if it handles drag-drop
find src -name "LayoutView.vue" -type f

# Check LayoutView implementation for drag-drop handling
cat -n src/views/LayoutView.vue | head -150

# Verify: does LayoutView have `@drop` or dragDrop handling?
rg -n "@drop|dragDrop|useDropZone" src/views/LayoutView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 253


🏁 Script executed:

# Check LayoutView.vue at the correct path
cat -n src/components/appMode/layout/LayoutView.vue | head -150

# Verify: does LayoutView have `@drop` or dragDrop handling?
rg -n "@drop|dragDrop|useDropZone" src/components/appMode/layout/LayoutView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 6699


🏁 Script executed:

# Check for drop/drag handling further down in LayoutView.vue
rg -n "@drop|dragDrop|handleDragDrop|useDropZone" src/components/appMode/layout/LayoutView.vue

# Also check full length to see if file ends shortly
wc -l src/components/appMode/layout/LayoutView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 118


🏁 Script executed:

# Check LayoutView template structure for drop handlers
tail -n 100 src/components/appMode/layout/LayoutView.vue | head -n 80

# More specifically, search the entire file for drop/drag patterns
rg -n -i "drop|drag" src/components/appMode/layout/LayoutView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3588


🏁 Script executed:

# Check the outer container div in LinearView - does it have `@drop`?
sed -n '110,125p' src/views/LinearView.vue

# Also verify: is there anything in LayoutView's template root that might catch drops?
sed -n '600,640p' src/components/appMode/layout/LayoutView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2160


🏁 Script executed:

# Final verification: confirm outer container has no `@drop` binding in showLayoutView path
sed -n '121,151p' src/views/LinearView.vue

# And confirm the old branch HAS `@drop` on the center panel
sed -n '189,210p' src/views/LinearView.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1940


Add drag-drop workflow import handling to the new App Mode layout.

The new showLayoutView branch does not support drag-dropping workflow files (.json/.png) onto the app. The previous branch had @drop="dragDrop" bound to the center panel, which called linearWorkflowRef.value?.handleDragDrop(e). In the new branch:

  • Neither the outer container nor LayoutView binds @drop
  • LinearControls is not mounted, so linearWorkflowRef never exists — even if a @drop handler were added, it would silently fail
  • LayoutView contains no workflow file drop handling (its drag references are for panel repositioning only)

Either wire @drop="dragDrop" to a container in the new branch and ensure LinearControls (or equivalent) is mounted to populate linearWorkflowRef, or implement workflow import handling directly in LayoutView.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/LinearView.vue` around lines 121 - 151, The new showLayoutView
branch removed the drag-drop entry so workflow files cannot be imported; add
drop handling and ensure the handler has a valid target by either (A) wiring
`@drop`="dragDrop" and `@dragover.prevent` on the center container (e.g., the
SplitterPanel or the parent div used when showLayoutView is true) and mounting
LinearControls so linearWorkflowRef exists, then call
linearWorkflowRef.value?.handleDragDrop(e) inside dragDrop, or (B) implement
equivalent file-import logic directly inside LayoutView (expose a handleDragDrop
method on the LayoutView component and call it from the bound `@drop`) so the drop
works even when LinearControls is not mounted; reference LinearControls,
linearWorkflowRef, dragDrop, and LayoutView to locate where to add/route the
handler.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a false positive. The old @drop="dragDrop" binding didn't route workflow file imports — those are handled at the document level by app.addDropHandler() in src/scripts/app.ts:589, which works regardless of which LinearView branch renders.

What dragDrop actually did was route per-widget drops through LinearControls.handleDragDropAppModeWidgetList.handleDragDrop (iterating selected node widgets). In the new branch those still work because each InputCell.vue wraps its widgets in a <DropZone>. The routing moved from one central relay to per-cell zones.

No code change needed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. Verify document-level drop handler in app.ts around line 589
sed -n '580,600p' src/scripts/app.ts

# 2. Check what dragDrop/handleDragDrop in LinearControls actually does
rg -n "handleDragDrop" src/renderer/extensions/linearMode/LinearControls.vue -A 10 | head -40

# 3. Confirm DropZone usage in InputCell.vue
rg -n "DropZone" src/components/appMode/layout/cells/InputCell.vue

Length of output: 1462


@eliheuer, you're right — I've verified all three points:

  1. Workflow file imports: app.addDropHandler() in src/scripts/app.ts registers a document-level drop listener that handles .json/.png workflow imports regardless of which LinearView branch is active. ✅
  2. dragDrop scope: LinearControls.handleDragDrop (Line 74) just delegates to widgetListRef.value?.handleDragDrop(e) — it was only ever a relay for per-widget drops, not file imports. ✅
  3. Per-widget drops in the new branch: InputCell.vue (Lines 65–70) wraps NodeWidgets in <DropZone>, so per-cell drops work without the central relay. ✅

The comment was a false positive. Sorry for the noise!

( ·̥̥̥̥̥̥̥̥ω·̥̥̥̥̥̥̥̥ ) noted for future reviews


✏️ Learnings added
Learnt from: eliheuer
Repo: Comfy-Org/ComfyUI_frontend PR: 11317
File: src/views/LinearView.vue:121-151
Timestamp: 2026-04-22T17:12:51.213Z
Learning: In `src/views/LinearView.vue`, the `dragDrop` / `drop` binding on the center splitter panel in the old `LinearView` branch was NOT responsible for workflow file imports (.json/.png). Workflow file imports are handled at the document level by `app.addDropHandler()` in `src/scripts/app.ts` via `useEventListener(document, 'drop', ...)`, which fires regardless of which branch renders. The `dragDrop` relay only routed per-widget drops to `LinearControls.handleDragDrop` → `AppModeWidgetList.handleDragDrop`. In the new `showLayoutView` branch, per-widget drops work through per-cell `<DropZone>` wrappers in `InputCell.vue`. Do not flag the absence of `drop="dragDrop"` in the new App Mode layout branch as a regression.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: eliheuer
Repo: Comfy-Org/ComfyUI_frontend PR: 11317
File: src/components/appMode/layout/panels/PanelBlockList.vue:0-0
Timestamp: 2026-04-22T17:03:35.095Z
Learning: In `src/components/appMode/layout/panels/PanelBlockList.vue`, the block-drag grip element was intentionally demoted from a `<button>` to a `<span aria-hidden="true">` (commit 385b611a0) to remove it from the accessibility tree pending a proper keyboard-reorder implementation. The `linearMode.blockDragReorderAria` i18n key is kept in `src/locales/en/main.json` as a forward-compatible placeholder for when keyboard reorder lands as a follow-up. Do not flag the aria-hidden grip or the unused translation key as issues.

Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 11541
File: src/renderer/extensions/vueNodes/components/LGraphNode.vue:816-825
Timestamp: 2026-04-22T03:35:47.385Z
Learning: In `src/renderer/extensions/vueNodes/components/LGraphNode.vue`, the `handleDrop` function intentionally does NOT wrap `await node.onDragDrop(event)` in a try/catch or route errors through `toastErrorHandler`. Previously no error popup existed for this path, and adding one would be new behavior out of scope. Do not flag the missing try/catch around `node.onDragDrop` in `handleDrop` as an issue; if error surfacing is needed it should be a separate PR.

Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 11495
File: src/extensions/core/load3d/load3dContextMenuGuard.ts:52-66
Timestamp: 2026-04-21T03:30:28.591Z
Learning: In `src/extensions/core/load3d/load3dContextMenuGuard.ts`, the contextmenu handler intentionally returns early (before `preventDefault`/`stopPropagation`) when a drag is detected. This is pre-refactor behavior that was deliberately preserved. Do not flag the early return on drag as a bug — the native context menu appearing on drag is accepted behavior in this codebase.

Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 11538
File: src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenuItem.vue:47-51
Timestamp: 2026-04-22T02:47:43.665Z
Learning: In Comfy-Org/ComfyUI_frontend, `findServerPreviewUrl` (src/platform/assets/utils/assetPreviewUtil.ts) is typed `Promise<string | null>` and contains an internal try/catch that returns `null` on any error — it never rejects. Call sites (e.g., FormDropdownMenuItem.vue, Media3DTop.vue) do not need an outer try/catch; error handling lives entirely inside the utility.

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-20T01:57:38.157Z
Learning: Before implementing user-facing features, consult the Comfy Design Standards Figma file

Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In components that use the cn utility from '@/utils/tailwindUtil' with tailwind-merge, rely on the behavior that conflicting Tailwind classes are resolved by keeping the last one. For example, cn('base-classes bg-default', propClass) will have any conflicting background class from propClass override bg-default. This additive pattern is intentional and aligns with the shadcn-ui convention; ensure you document or review expectations accordingly in Vue components.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8195
File: src/platform/assets/components/MediaAssetFilterBar.vue:16-16
Timestamp: 2026-01-21T01:28:27.626Z
Learning: In Vue templates (Vue 3.4+ with the build step), when binding to data or props that are camelCase (e.g., mediaTypeFilters), you can use kebab-case in the template bindings (e.g., :media-type-filters). This is acceptable and will resolve to the corresponding camelCase variable. Do not require CamelCase in template bindings.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8090
File: src/platform/assets/components/modelInfo/ModelInfoField.vue:8-11
Timestamp: 2026-01-22T02:28:58.105Z
Learning: In Vue 3 script setup, props defined with defineProps are automatically available by name in the template without destructuring. Destructuring the result of defineProps inside script can break reactivity; prefer accessing props by name in the template. If you need to use props in the script, reference them via the defined props object rather than destructuring, or use toRefs when you intend to destructure while preserving reactivity.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8497
File: src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue:223-236
Timestamp: 2026-02-01T21:10:29.567Z
Learning: In Vue single-file components, do not review or require edits to comments. Favor self-documenting code through clear naming, strong types, and explicit APIs. If a comment is misleading or outdated, consider removing it, but avoid suggesting adding or fixing comments. This guideline aligns with preferring code clarity over comment maintenance across all .vue files.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8592
File: src/components/topbar/WorkflowExecutionIndicator.vue:28-28
Timestamp: 2026-02-03T21:35:40.889Z
Learning: In Vue single-file components where the i18n t function is only used within the template, prefer using the built-in $t in the template instead of importing useI18n and destructuring t in the script. This avoids unnecessary imports when t is not used in the script. If you need i18n in the script (Composition API), only then use useI18n and access t from its returned object. Ensure this pattern applies to all Vue components with template-only i18n usage.

Learnt from: Yourz
Repo: Comfy-Org/ComfyUI_frontend PR: 8548
File: src/components/common/TreeExplorerV2Node.vue:9-47
Timestamp: 2026-02-07T14:47:06.751Z
Learning: In all Vue files that render Reka UI context menu components (ContextMenuRoot, ContextMenuTrigger, etc.), avoid dynamic component wrapping with <component :is="...">. Use conditional rendering with v-if / v-else to render separate branches, even if it results in some template duplication. This improves readability and maintainability across files that use these components.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8753
File: src/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue:17-28
Timestamp: 2026-02-09T03:24:47.113Z
Learning: When destructuring reactive properties from Pinia stores in Vue components, use storeToRefs() to preserve reactivity. Example: const store = useCanvasStore(); const { canvas } = storeToRefs(store); access as canvas.value (e.g., canvas.value). Ensure you import storeToRefs from 'pinia' and use it wherever you destructure store properties in the setup function.

Learnt from: pythongosssss
Repo: Comfy-Org/ComfyUI_frontend PR: 8775
File: src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue:62-68
Timestamp: 2026-02-10T17:59:25.893Z
Learning: In Vue 3 single-file components, code defined outside the script setup scope cannot access variables or helpers defined inside setup. If a default function (e.g., defineModel) is hoisted outside script setup, it will not be able to call setup helpers or reference setup-scoped variables. Place such logic inside setup or expose necessary values via returns/defineExpose to be accessible.

Learnt from: Yourz
Repo: Comfy-Org/ComfyUI_frontend PR: 9085
File: src/components/sidebar/tabs/NodeLibrarySidebarTabV2.vue:45-50
Timestamp: 2026-02-27T09:39:47.145Z
Learning: In Vue components that use Reka UI primitives such as DropdownMenuTrigger and SelectTrigger with as-child, it is acceptable to render a raw <button> when the styling is context-specific and there is no existing Button variant to map to. The as-child pattern preserves accessibility while enabling composition. Do not create single-use Button variants for these cases; prefer inline styling with a raw button for clarity and to avoid unnecessary abstraction. This guideline applies to all Vue components (files ending in .vue) in projects using Reka UI.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 9427
File: src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenuFilter.vue:33-33
Timestamp: 2026-03-06T00:53:28.835Z
Learning: When reviewing code, note that the enforce-canonical-classes (better-tailwindcss) rule may auto-fix Tailwind v3 !class-name syntax by converting it to v4 class-name! syntax. Do not treat these auto-fixed class-name! instances as newly introduced issues; the perceived change is in syntax placement, not in usage or intent. This guidance applies across all .vue and .ts files in the repository.

Learnt from: sonnybox
Repo: Comfy-Org/ComfyUI_frontend PR: 9446
File: src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.vue:45-45
Timestamp: 2026-03-06T01:55:00.013Z
Learning: Treat wrap-break-word as a valid Tailwind CSS utility for overflow-wrap: break-word in Tailwind v4+ projects. Do not flag this class as invalid in any Vue (.vue) or TypeScript (.ts/.tsx) files within the repository (e.g., Comfy-Org/ComfyUI_frontend) or other Tailwind v4+ projects. When reviewing, verify that the class is used to enable word breaking in long text content and reference the Tailwind docs: https://tailwindcss.com/docs/overflow-wrap.

Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 9749
File: src/platform/assets/components/MediaAssetContextMenu.vue:88-93
Timestamp: 2026-03-11T19:12:43.621Z
Learning: For Vue components using PrimeVue ContextMenu, the root element merges all passthrough attributes via v-bind="ptmi('root')". Passing an id through :pt="{ root: { id: someId } }" will reliably set the root element's id, allowing document.getElementById(someId) to reference the overlay. Do not flag this pattern as unreliable in reviews for components using PrimeVue ContextMenu with pt.root.id passthrough (e.g., src/platform/assets/components/MediaAssetContextMenu.vue).

Learnt from: Yourz
Repo: Comfy-Org/ComfyUI_frontend PR: 9965
File: src/platform/cloud/onboarding/CloudSubscriptionRedirectView.vue:77-79
Timestamp: 2026-03-15T15:24:17.291Z
Learning: Maintain narrowly scoped PRs by limiting changes to the stated fix within a single PR. Do not bundle cross-file refactors (e.g., adding concurrency guards across composables like useBillingContext) into a targeted bug-fix PR; instead propose these as separate follow-up issues or PRs. This guidance applies to Vue components (e.g., CloudSubscriptionRedirectView.vue) and other files across the repository. When a change touches multiple files or concerns broader architecture, split into separate, well-scoped PRs to improve reviewability and risk assessment.

<Splitter
v-else
Expand Down