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
chore(app-mode): trim small net-new files (-82 lines)
Comment-trim pass on the seven smaller net-new files the previous
pass skipped. Same pattern: drop paraphrase comments, tighten doc
preambles, keep load-bearing rationale.
  • Loading branch information
eliheuer committed Apr 27, 2026
commit bd4f9eff5e36b83d75b785f5a3b84968697e91f6
38 changes: 13 additions & 25 deletions src/components/appMode/layout/panels/PanelDragPreview.vue
Original file line number Diff line number Diff line change
@@ -1,40 +1,28 @@
<script setup lang="ts">
/**
* PanelDragPreview — translucent brand-blue rectangle rendered at the
* snap-target preset's bounds while a panel is being dragged. Appears
* above the live panel to signal where the panel will land on release.
*
* Passing `panelHeight` makes the preview match the live panel's
* content-fit height instead of stretching to the preset's full
* `max-height`; each preset's `max-height` still caps the preview on
* small viewports.
* Translucent outline rendered at the snap-target preset's bounds
* during a panel drag. Sits above the live panel to signal where it
* will land on release.
*/
import { computed } from 'vue'

import { PANEL_PRESET_CLASSES } from './panelPresetClasses'
import { isDockPreset } from './panelTypes';
import type { PanelPreset } from './panelTypes';
import { isDockPreset } from './panelTypes'
import type { PanelPreset } from './panelTypes'

const props = defineProps<{
preset: PanelPreset
panelHeight?: number
panelWidth?: number
}>()

// Preset classes are shared with FloatingPanel via
// `PANEL_PRESET_CLASSES` so the drop preview always lands where the
// live panel will.
const presetClass = computed(() => PANEL_PRESET_CLASSES[props.preset])
const isDocked = computed(() => isDockPreset(props.preset))
// Match the live panel's rendered dimensions so the preview lands the
// same size the panel will — important when the user has drag-resized
// the dock wider than the default. Dock presets span the full slot via
// `top` + `bottom` CSS anchors, so leave the height out for them; an
// explicit `height` would override the bottom anchor and shrink the
// preview to the live panel's content-fit height (which is what was
// happening when dragging from a float corner toward a dock — the
// preview rendered as a short rectangle at the top instead of a
// full-height column).
// Match the live panel's rendered dimensions so the preview lands at
// the size the panel will. Dock presets span the full slot via
// top+bottom anchors, so leave height out for them — an explicit
// height would override the bottom anchor and shrink the preview to
// the live panel's content-fit height.
const sizeStyle = computed(() => {
const style: Record<string, string> = {}
if (!isDocked.value && props.panelHeight != null)
Expand All @@ -47,9 +35,9 @@ const sizeStyle = computed(() => {
<template>
<div
:class="[
// Base chrome: absolute, accent-temp outline + 30% tint fill,
// content non-interactive, animated through the shared layout
// duration/easing so the preview tweens between presets.
// Accent-temp outline + 30% tint fill, animated through the
// shared layout duration/easing so the preview tweens between
// presets when the snap target changes.
'pointer-events-none absolute z-20',
'w-(--panel-dock-width,440px)',
'rounded-[10px] border-2 border-(--color-app-mode-accent-temp)',
Expand Down
10 changes: 3 additions & 7 deletions src/components/appMode/layout/panels/panelTypes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Panel + block config types for the semi-customizable floating panel.
* State is in-memory only for now; persistence is a follow-up.
* In-memory only for now; persistence is a follow-up.
*/

export type PanelPreset =
Expand All @@ -11,12 +11,8 @@ export type PanelPreset =
| 'float-tl'
| 'float-bl'

/**
* Preset-shape predicates. Centralized so consumers don't reinvent
* `preset === 'right-dock' || preset === 'left-dock'` (or the more
* fragile `startsWith` / `endsWith` parses) at every call site — the
* `PanelPreset` union stays the single source of truth.
*/
// Preset-shape predicates — keep consumers off `startsWith` /
// `endsWith` parses; the union stays the single source of truth.
export const isDockPreset = (p: PanelPreset): boolean =>
p === 'right-dock' || p === 'left-dock'

Expand Down
25 changes: 9 additions & 16 deletions src/components/appMode/layout/panels/usePanelResize.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
/**
* usePanelResize — pointer-driven width resize for FloatingPanel when
* docked. Snaps to whole cell + gutter increments so the panel always
* lands on the layout grid. Activates on pointerdown over an inner-
* edge hit area (caller wires `startResize` to that element).
*
* Clamped to [MIN_CELLS, MAX_CELLS]; the min matches the default dock
* width so the panel can never be narrower than its non-resizable
* baseline.
* Pointer-driven width resize for FloatingPanel's dock presets.
* Snaps to whole cell+gutter steps so the panel lands on the grid.
* Clamped to [MIN_CELLS, MAX_CELLS] (min = default dock width).
*/
import { useEventListener, useWindowFocus } from '@vueuse/core'
import { ref, watch } from 'vue'
Expand All @@ -22,8 +17,8 @@ interface UsePanelResizeOptions {
const MIN_CELLS = 8
const MAX_CELLS = 19

/** Read the grid step (cell + gutter, in px) from design-system tokens
* at runtime so theme overrides take effect without a constant sync. */
/** Read cell + gutter from design-system tokens at runtime so theme
* overrides take effect without a constant sync. */
function readGridStep(): number {
if (typeof document === 'undefined') return 56
const cs = getComputedStyle(document.documentElement)
Expand Down Expand Up @@ -60,9 +55,8 @@ export function usePanelResize(opts: UsePanelResizeOptions) {

useEventListener(window, 'pointermove', (e: PointerEvent) => {
if (!isOurPointer(e)) return
// For right-dock the panel's inner edge is on the LEFT — dragging
// the pointer left should widen the panel (negative clientX delta
// → more cells). For left-dock it's the opposite.
// Right-dock's inner edge is on the LEFT, so a leftward pointer
// delta widens the panel; left-dock is opposite.
const delta = e.clientX - startX
const widenPx = opts.side.value === 'right' ? -delta : delta
const deltaCells = Math.round(widenPx / gridStep)
Expand All @@ -83,9 +77,8 @@ export function usePanelResize(opts: UsePanelResizeOptions) {
reset()
})

// Abandon on window blur so a resize doesn't survive alt-tab / OS
// modals waiting for a pointerup that never arrives. Matches
// usePanelDrag's behavior for the same reason.
// Abandon on window blur so a resize can't get stuck waiting for a
// pointerup that never arrives (alt-tab, OS modal, etc.).
const focused = useWindowFocus()
watch(focused, (nowFocused) => {
if (!nowFocused && activePointerId !== null) reset()
Expand Down
61 changes: 17 additions & 44 deletions src/components/builder/BuilderBackdrop.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
<script setup lang="ts">
/**
* BuilderBackdrop — dot-grid canvas + LinearPreview backdrop for the
* builder's Preview step. Mirrors LayoutView's backdrop exactly so the
* Preview visually reads as App Mode: dot-grid canvas color behind,
* LinearPreview in front (which internally shows LinearArrange's dashed
* output-area marker in arrange mode, or the actual output when run).
*
* Mounts only in the arrange step: during Inputs / Outputs the user
* clicks on graph-canvas nodes, so the graph has to stay visible.
* In arrange (Preview), graph interaction isn't meaningful — this
* backdrop replaces the graph canvas so authors see a clean
* App-Mode-style preview of where their outputs and inputs will sit.
*
* Pan/zoom — same workspace-transform pattern as LayoutView, sharing
* `appModeStore.viewport*` so the user's zoom level survives a
* builder ↔ app-mode round trip. AppChrome's nav cluster routes its
* builder handlers to App Mode actions while `isArrangeMode` is true,
* so the cluster operates this backdrop instead of the hidden graph.
* Dot-grid canvas + LinearPreview backdrop for the builder's Preview
* step. Mirrors LayoutView's backdrop so Preview reads as App Mode.
* Mounts only in arrange (graph interaction isn't meaningful there).
* Shares `appModeStore.viewport*` with App Mode so zoom level
* survives a round trip.
*/
import { useEventListener } from '@vueuse/core'
import { storeToRefs } from 'pinia'
Expand Down Expand Up @@ -80,11 +68,10 @@ const workspaceTransform = computed(
`scale(${viewportScale.value})`
)

// Same LOD-doubling math LayoutView uses — keep the dot grid pitch
// from collapsing into noise when zoomed out, while letting it
// scale up when zoomed in.
const DOT_SIZE_PX = 24
const MIN_GRID_SPACING_PX = 16
// LOD-doubling keeps the grid pitch from collapsing into noise
// when zoomed out (same trick LayoutView used pre-LOD-redesign).
const gridSpacing = computed(() => {
let s = DOT_SIZE_PX * viewportScale.value
if (!(s > 0)) return DOT_SIZE_PX
Expand All @@ -94,16 +81,9 @@ const gridSpacing = computed(() => {
</script>

<template>
<!-- Same dot-grid + canvas-color treatment LayoutView uses so Preview
reads as App Mode. Sits between the graph canvas (below) and the
chrome / panel layers (above): z-50 covers the canvas but sits
under AppChrome (z-1/90), FloatingPanel (z-100), BuilderToolbar
and BuilderFooterToolbar. Sidebar-width offset keeps the Comfy
sidebar icon strip visible during arrange. The dot grid lives on
this outer element with dynamic background-size + position
driven by the viewport vars — same trick as LayoutView so the
grid pattern visually matches the transformed content without
this element itself needing to scale. -->
<!-- z-50 covers the graph canvas but sits under AppChrome /
FloatingPanel / BuilderToolbar. Sidebar-width offset keeps
the Comfy sidebar visible during arrange. -->
<div
v-if="isArrangeMode"
:class="[
Expand All @@ -118,10 +98,8 @@ const gridSpacing = computed(() => {
backgroundPosition: `${viewportOffsetX}px ${viewportOffsetY}px`
}"
>
<!-- Workspace layer: holds the transform + wheel/pointer handlers
so the cluster (when in arrange mode) and direct gestures
drive the same `appModeStore.viewport*` state LayoutView
reads. The dot grid above pans + scales in lockstep. -->
<!-- Workspace layer: holds transform + wheel/pointer handlers,
driving the same viewport state LayoutView reads. -->
<div
ref="bgRef"
class="builder-backdrop__workspace absolute inset-0 flex flex-col"
Expand All @@ -135,20 +113,15 @@ const gridSpacing = computed(() => {
</div>
</template>

<!-- Exception (docs/guidance/vue-components.md §Styling): :deep(*) is
the only practical way to re-enable pointer events on the slotted
<LinearPreview> subtree. The root is pointer-events-none so graph/
chrome clicks fall through empty builder-backdrop space; slotted
content needs pointer-events-auto so LinearArrange's clickable
zones still register. We don't own LinearPreview's render tree, so
we can't push the rule into that component's template. -->
<!-- Documented exception (docs/guidance/vue-components.md §Styling):
:deep(*) re-enables pointer events on the slotted LinearPreview
subtree because we don't own its render tree. Root is
pointer-events-none so empty backdrop space lets clicks fall
through to graph/chrome. -->
<style scoped>
.builder-backdrop :deep(*) {
pointer-events: auto;
}
/* The workspace layer is itself a child of the pointer-events-none
root, so re-enable pointer events here too — wheel + pointerdown
need to reach our handlers. */
.builder-backdrop__workspace {
pointer-events: auto;
transform-origin: center;
Expand Down
47 changes: 13 additions & 34 deletions src/components/builder/BuilderPanel.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
<script setup lang="ts">
/**
* BuilderPanel — floating right-dock panel for the App Builder flow.
*
* Renders the same PanelBlockList App Mode shows, via the same shared
* state in appModeStore, so the builder is WYSIWYG by construction:
* what you see while picking / arranging inputs is exactly what App
* Mode renders at runtime. InputCell's `builder` variant adds the ⋯
* Rename/Remove menu and inerts the widget body so preview inputs
* aren't typed into while arranging.
*
* Panel preset, collapse state, and `panelRows` all live on
* appModeStore; moving or rearranging in either view updates both.
* Graph-canvas click-to-select overlays (widget highlights + output
* rings) live in AppBuilder.vue — this component owns the panel
* surface only.
*
* Run + Number-of-runs live in BuilderChrome (upper-right cluster) to
* match App Mode's runtime chrome, not in this panel.
* Floating right-dock panel for the App Builder flow. Renders the
* same PanelBlockList as App Mode, reading from the same appModeStore
* — the builder is WYSIWYG with App Mode by construction. InputCell's
* `builder` variant adds rename/remove and inerts the widget body so
* inputs aren't typed into during arrangement.
*/
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
Expand All @@ -37,8 +25,7 @@ const { panelPreset, panelCollapsed, panelRows } = storeToRefs(appModeStore)
const { isSelectInputsMode, isSelectOutputsMode } = useAppMode()
const { inputEntryMap, moveBlock } = useAppPanelLayout()

// Match App Mode's panel title (the workflow filename) so builder + App
// Mode read as the same surface across modes.
// Match App Mode's panel title (workflow filename) for WYSIWYG parity.
const panelTitle = computed(() => {
const path = workflowStore.activeWorkflow?.path
if (!path) return t('linearMode.builder.title')
Expand All @@ -57,18 +44,12 @@ const emptyCopy = computed(() => {
</script>

<template>
<!-- Positioned ancestor so FloatingPanel's preset-based absolute
positioning resolves inside the builder just like it does inside
.layout-view. Left edge is offset by `--sidebar-width` so the
wrapper's coordinate system starts after the Comfy sidebar —
matching App Mode, where LayoutView is a flex sibling of the
sidebar and so already starts past it. With both contexts sharing
the same origin, the FloatingPanel preset classes use `left: 0`
and land flush against the sidebar in either mode. The root is
pointer-events-none so clicks on empty chrome areas fall through
to the graph canvas; FloatingPanel opts back into pointer-
events-auto in its own root class list so the panel itself still
captures input. -->
<!-- Positioned ancestor so FloatingPanel's absolute positioning
resolves correctly. Left offset by `--sidebar-width` so the
coordinate system starts past the Comfy sidebar — matching
App Mode, where LayoutView is a flex sibling of the sidebar
and already starts past it. Root is pointer-events-none so
empty chrome space falls through to the graph canvas. -->
<div
class="pointer-events-none fixed top-(--workflow-tabs-height) right-0 bottom-0 left-(--sidebar-width,0px) z-100"
>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expand All @@ -86,9 +67,7 @@ const emptyCopy = computed(() => {
@reorder="moveBlock"
/>

<!-- Empty-state picker target. Dashed affordance matches the original
builder signal that this is a "drop zone" for widget picks from
the canvas; radius + colors align with design tokens. -->
<!-- Empty-state drop-zone affordance for widget picks from the canvas. -->
<div
v-else
:class="[
Expand Down
26 changes: 10 additions & 16 deletions src/renderer/extensions/linearMode/outputWindowStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,23 @@ import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
import type { ResultItemImpl } from '@/stores/queueStore'

/**
* One floating output window in the App Mode workspace.
*
* Lifecycle is fed by `linearOutputStore.activeWorkflowInProgressItems`:
* windows spawn in `'skeleton'`, transition to `'latent'` / `'image'`,
* and stay in the store after the source item gets absorbed out of
* the in-progress list — that's what gives the canvas its moodboard
* accumulation across runs.
* One floating output window. Fed by
* `linearOutputStore.activeWorkflowInProgressItems`: spawns at
* `'skeleton'`, transitions to `'latent'` / `'image'`, and stays
* after the source item is absorbed out of the in-progress list —
* that's what gives the canvas its moodboard accumulation.
*/
export interface OutputWindowEntry {
id: string
/** Used to match the window back to its `AssetItem` once the run
* lands in `outputs.media`. */
/** Matches the window back to its `AssetItem` via `outputs.media`. */
jobId?: string
state: 'skeleton' | 'latent' | 'image'
latentPreviewUrl?: string
output?: ResultItemImpl
/** Populated by `useOutputWindowSync` once the asset shows up in
* `outputs.media`. Unlocks rerun / reuse-params and the Cloud
* `asset_hash` URL fix. */
/** Populated by useOutputWindowSync; unlocks rerun + reuse-params
* and the Cloud asset_hash URL fix. */
asset?: AssetItem
/** Workspace coordinates (pre-transform). LayoutView's transform
* handles zoom/pan. */
/** Workspace coordinates (pre-transform). */
position: { x: number; y: number }
zIndex: number
}
Expand Down Expand Up @@ -90,8 +85,7 @@ export const useOutputWindowStore = defineStore('appModeOutputWindow', () => {
function promote(id: string): void {
const w = windows.value.find((w) => w.id === id)
if (!w) return
// No-op when already topmost — avoids unbounded zIndex climb from
// idle clicks on the focused window.
// Skip when already topmost — caps zIndex climb from idle clicks.
const maxZ = windows.value.reduce(
(m, x) => (x.zIndex > m ? x.zIndex : m),
0
Expand Down
Loading