Conversation
There was a problem hiding this comment.
Pull request overview
Updates the Lite workspace UI to a vertical “sections” layout and introduces stack-level selection to align navigation/shortcuts and operation-source modeling with the new structure.
Changes:
- Reworks workspace primary panel layout from horizontal lanes to a vertical sections list, including a new stack header row.
- Adds a new
Stackitem/source variant across selection, navigation outline, shortcut scopes, and operation source resolution/labeling. - Tweaks shared row button styling and adjusts preview panel sizing defaults.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/lite/ui/src/routes/project/$id/workspace/route.tsx | Renders vertical sections, adds stack header row selection, scrolls selected rows into view, and updates button styling usage. |
| apps/lite/ui/src/routes/project/$id/workspace/route.module.css | Introduces .sections layout + shared row button styles; removes old lane/segment button styles and updates stack/menu action styling. |
| apps/lite/ui/src/routes/project/$id/workspace/WorkspaceShortcuts.ts | Adds Stack scope so keyboard shortcuts can target stack rows. |
| apps/lite/ui/src/routes/project/$id/workspace/WorkspaceModel.ts | Adds stack items into the navigation outline so stacks become section headers. |
| apps/lite/ui/src/routes/project/$id/workspace/ResolvedOperationSource.ts | Adds Stack resolved-operation-source variant and resolver handling. |
| apps/lite/ui/src/routes/project/$id/workspace/OperationSourceLabel.tsx | Adds a label for Stack operation sources. |
| apps/lite/ui/src/routes/project/$id/workspace/OperationSource.ts | Adds Stack operation source variant and item→source mapping. |
| apps/lite/ui/src/routes/project/$id/workspace/Item.ts | Adds Stack to the selectable item union + identity key support. |
| apps/lite/ui/src/routes/project/$id/ProjectPreviewLayout.tsx | Adjusts panel sizing defaults (primary minSize and preview defaultSize). |
| Stack: (selectedItem) => | ||
| stackDefaultModeScope({ | ||
| bindings: primaryPanelBindings, | ||
| context: selectedItem, | ||
| }), |
There was a problem hiding this comment.
Stack items currently use primaryPanelBindings, which includes the m/r shortcuts to enter Move/Rub modes. But operationModeToOperation/ResolvedOperationSource don’t produce any operations for a Stack source (it resolves to _tag: "Stack" and every operation mapping returns null), so entering these modes from a selected stack becomes a dead-end UX (mode activates but can never run an operation). Consider removing EnterMoveMode/EnterRubMode from the stack scope bindings, or guard in handleItemSelectionAction to ignore those actions when selectedItem._tag === "Stack" until stack operations are implemented.
1170222 to
07875d2
Compare
07875d2 to
149a006
Compare
| {headInfo.stacks.map((stack) => ( | ||
| <StackC | ||
| key={stack.id} | ||
| branchRenameFormRef={branchRenameFormRef} | ||
| commitMessageFormRef={commitMessageFormRef} | ||
| operationMode={operationMode} | ||
| projectId={project.id} | ||
| selectedItem={selectedItem} | ||
| stack={stack} | ||
| workspaceMode={workspaceMode} | ||
| navigationIndex={navigationIndex} | ||
| /> | ||
| ))} |
There was a problem hiding this comment.
headInfo.stacks contains Stack objects whose id is typed as string | null. Rendering <StackC key={stack.id} ...> and then doing const stackId = stack.id! will crash at runtime if a null-id stack ever appears, and also produces an invalid/null React key. Filter out stacks with id == null here (and/or handle the null case inside StackC) and ensure the key is always a non-null stable string.
| {headInfo.stacks.map((stack) => ( | |
| <StackC | |
| key={stack.id} | |
| branchRenameFormRef={branchRenameFormRef} | |
| commitMessageFormRef={commitMessageFormRef} | |
| operationMode={operationMode} | |
| projectId={project.id} | |
| selectedItem={selectedItem} | |
| stack={stack} | |
| workspaceMode={workspaceMode} | |
| navigationIndex={navigationIndex} | |
| /> | |
| ))} | |
| {headInfo.stacks | |
| .filter((stack): stack is Stack & { id: string } => stack.id != null) | |
| .map((stack) => ( | |
| <StackC | |
| key={stack.id} | |
| branchRenameFormRef={branchRenameFormRef} | |
| commitMessageFormRef={commitMessageFormRef} | |
| operationMode={operationMode} | |
| projectId={project.id} | |
| selectedItem={selectedItem} | |
| stack={stack} | |
| workspaceMode={workspaceMode} | |
| navigationIndex={navigationIndex} | |
| /> | |
| ))} |
| <ContextMenu.Root disabled={workspaceMode._tag !== "Default"}> | ||
| <ContextMenu.Trigger | ||
| render={ | ||
| <button | ||
| type="button" | ||
| className={classes(styles.itemRowButton, styles.sectionButton)} | ||
| onClick={() => { | ||
| dispatch(projectActions.selectItem({ projectId, item })); | ||
| }} | ||
| > | ||
| Stack | ||
| </button> | ||
| } | ||
| /> | ||
| <ContextMenu.Portal> | ||
| <ContextMenu.Positioner> | ||
| <StackMenuPopup projectId={projectId} stackId={stackId} /> | ||
| </ContextMenu.Positioner> | ||
| </ContextMenu.Portal> | ||
| </ContextMenu.Root> | ||
| <Menu.Root> | ||
| <Menu.Trigger className={styles.stackMenuTrigger} aria-label="Stack menu"> | ||
| <Menu.Trigger className={styles.itemRowAction} aria-label="Stack menu"> | ||
| <MenuTriggerIcon /> | ||
| </Menu.Trigger> | ||
| <Menu.Portal> |
There was a problem hiding this comment.
ContextMenu.Root is disabled when workspaceMode isn’t Default, but the adjacent Menu.Root/Menu.Trigger remains enabled. This makes the stack menu inconsistently accessible (button works while context menu is disabled). Consider applying the same disable/visibility logic to the Menu trigger (or removing the ContextMenu disable) so both entry points behave the same.
No description provided.