Skip to content

refactor(ui): action-first flow with immediate save#2340

Merged
mariux merged 32 commits into
mainfrom
worktree-more-ui-changes
Apr 13, 2026
Merged

refactor(ui): action-first flow with immediate save#2340
mariux merged 32 commits into
mainfrom
worktree-more-ui-changes

Conversation

@mariux

@mariux mariux commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces #2335 (closed). Combines the action-first UI restructure with immediate save and UX improvements.

From #2335 (base changes)

  • Restructure overview to action-first flow (Create, Reconfigure, Promote, Quit)
  • Grouped bundle selection with detail boxes and environment filters
  • Scrollable panels, breadcrumb navigation, keyboard shortcuts
  • Cloud login prompt, error dialogs, nested bundle creation

New in this PR

  • Immediate save: Reconfigure and Promote save on confirm (like Create), removing pending changes batch-save entirely
  • Session panel: Overview shows "Recently Changed" with grouped bundles, change kind tags, and direct reconfigure
  • Unified input form: Single list with inline diff — no Changed/Unchanged section split
  • Immutable inputs: Blue values, "immutable" tag, cursor skips them
  • Button flow: Save/Cancel at bottom, Back when no changes (reconfig); left/right + up/down navigation
  • ESC handling: Discard confirmation when reconfig has unsaved changes
  • Registry cleanup: Removed ui.Registry wrapper, use *config.Registry directly
  • Type system fix: Moved bundle-ref object→alias extraction from BundleType.Apply to UI layer
  • Upfront validation: checkBundleRefsResolved() replaces brittle string-matching error detection
  • Styling: Underlined hotkey letters, env tags in breadcrumbs, last-saved highlight
  • Dead code removed: view_edit.go, PendingChanges/ProposedChanges, SavedChange, and many unused fields

Test plan

  • Create: saves immediately, appears in session panel and reconfigure list
  • Reconfigure: saves immediately, session panel shows "reconfigured" tag
  • Promote: saves immediately, bundle appears in target env
  • Session panel: shows only changed envs, dimmed when unfocused, Enter reconfigures
  • Reconfig form: immutable fields skipped, changed values orange with diff
  • Reconfig form: cursor returns to edited item, Save/Cancel at bottom
  • Reconfig form: ESC with changes triggers discard confirmation
  • Promote form: Save always available, ESC goes back without confirmation
  • Quit exits immediately (no unsaved changes warning)
  • Bundle references resolve correctly after immediate save (nested bundles)

🤖 Generated with Claude Code


Note

Medium Risk
Changes core TUI workflow to write bundle instance YAML immediately (create/reconfigure/promote) and reload the registry in-session, so mistakes can persist to disk and affect subsequent resolution/filtering. UI navigation/rendering was substantially reworked, increasing risk of UX regressions across create/reconfig/promote flows.

Overview
Refactors the TUI to an action-first flow and removes the pending/batch save model: Create/Reconfigure/Promote now save bundle instance YAML immediately, reload the config.Registry after each save, and the overview shows a “Recently Changed” session panel that can jump straight into reconfigure.

Create selection is redesigned into a flat, sorted bundle list with a detail box, and Create adds a deferred environment picker when the chosen bundle requires an environment. Reconfigure/Promote selection adds environment-based filtering (cycle with e), richer detail boxes, and updated breadcrumbs/hotkeys.

InputsForm is unified into a single ordered list (no Changed/Unchanged split) with inline diffs, improved cursor/navigation (skips immutable/disabled inputs, wraps), and updated button logic (Save/Cancel; Back when no reconfig changes; ESC discard confirmation for reconfig). Bundle-ref handling is hardened by normalizing stored object values back to alias strings, rebinding bundle()/bundles() to the live registry during change evaluation, and adding checkBundleRefsResolved() for clearer missing-dependency errors.

Reviewed by Cursor Bugbot for commit 0a8f97f. Bugbot is set up for automated code reviews on this repo. Configure here.

mariux added 24 commits April 12, 2026 23:34
Restructure the `terramate ui` interactive flow from an upfront
environment selection to an action-first pattern where users pick
what to do before being asked contextual questions.

## Flow Changes

**Before:** Env → Action → Collection → Bundle → Inputs
**After:**  Action → Bundle (flat list) → [Env if needed] → Inputs

- Remove mandatory upfront environment selection screen
- Flatten Create from two-step Collection→Bundle picker into a
  single sorted list of all bundle variants across all sources
- Defer environment selection to after bundle pick, only when the
  bundle requires it (environments.required = true)
- Reconfigure/Promote show all items by default with an env filter
  toggle (press 'e') instead of gating behind env selection
- Promote computes promotable bundles across all environments

## UI Improvements

- Add sticky detail box with titled border showing bundle metadata
  (name, version, class, collection, source, path, env flow)
- Add dismissible error dialog overlay for bundle-not-enabled and
  other errors (centered modal, red border, any-key dismiss)
- Simplify list rows: Create shows name+version per line,
  Reconfigure/Promote use grouped layout with compact instance rows
- Update breadcrumbs: root="Terramate Catalyst", last segment bold,
  current action only (e.g. "Create MyBundle (staging)")
- Add ↑↓ navigation hints in help text
- Correct Esc navigation chain: Inputs → Env Select → Bundle List → Overview

## Env Filter (Reconfigure & Promote)

- Precomputed filter states: only envs with matching bundles are
  in the cycle, plus "env-less" for Reconfigure
- Press 'e' to cycle through filters, Esc to reset to "all"
- Breadcrumb shows active filter (e.g. "in staging", "to production")
- Help text previews the next filter env

## Technical Details

- New ViewCreateEnvSelect state for deferred env picker
- New flatBundleEntry type mapping flat rows to collection/bundle indices
- New envFilterState type for precomputed filter cycles
- New renderDetailBox with labeled fields, truncation, and separators
- loadBundleEvalContext now takes explicit env parameter
- buildAllPromoteBundles returns parallel bundle+targetEnv slices
- createBackView() helper for correct back-navigation
Create: add bundle description with indent, bold names, remove
excess padding, use separator between entries.

Reconfigure/Promote: flat per-instance scrolling (fixes groups
with many instances not scrolling), empty line between groups,
parameterized scrollWindowVar separator.

Reconfigure: always show Environment field (n/a for env-less)
to prevent detail box height flapping.
Fix gofmt alignment in ViewState constants.
Remove unused 'base' parameter from overlayErrorDialog.
Rename "Terramate Catalyst" to "Terramate UI" in breadcrumbs.

Align env flow labels (envA → envB) in Promote list by padding
alias names to the max width across all instances, so the arrow
columns line up regardless of alias length.
c: create, r: reconfigure, p: promote, q: quit
Shortcuts work when focus is on the commands section.
Help text shows available shortcuts at the bottom.
Pass innerWidth instead of contentWidth to renderDetailBox so the
detail box fills the full width inside the outer border. Previously
the scrollbar gutter was subtracted twice, making the detail box
narrower than the panel and causing uneven right-side spacing.
startNestedCreate now checks if the nested bundle needs env
selection after loadBundleDef, redirecting to ViewCreateEnvSelect
when environments are configured. Previously it unconditionally
set ViewCreateInput, leaving the input form uninitialized when
loadBundleDef deferred evaluation for env-requiring bundles.
Nested bundle creates (via bundle-ref inputs) now inherit the
parent's environment instead of re-asking. The deferred env
selection path in loadBundleDef is skipped when createStack is
non-empty, since nested bundles must live in the same env scope
as their parent.
Nested creates now show the full parent chain in the breadcrumb:
  Create VPC (staging) / Create Subnet / Create SecurityGroup
instead of the previous barely-visible "(for VPC)" suffix.

Supports arbitrary nesting depth via createStack traversal.
Show env in [] only on the current (last) breadcrumb level.
Remove the right-aligned env label from the header — it's
redundant now that env context is in the breadcrumb.
CreateFrame now saves and restores selectedCollIdx,
selectedBundleIdx, selectedBundleDefEntry, and
selectedBundleSource alongside flatBundleCursor and inputsForm.

Previously only the cursor and form were saved, so after a
nested create and restore, the bundle definition still pointed
to the nested bundle instead of the parent.
Create no longer batches changes as "pending" — bundles are saved
to disk immediately on confirm, then the registry is reloaded.
This fixes nested bundle references: the parent can now resolve
bundle() calls to dependencies created in the same session.

- All Create confirmations save + reload immediately
- NewCreateChange rebinds bundle()/bundles() functions to current
  registry so references to just-saved bundles resolve correctly
- Confirm button renamed to "Save"
- No more pending changes accumulating from Create actions
BundleRefWidget.FormatDisplay now handles both string values (alias)
and resolved bundle objects (extracts alias attribute). Also falls
back to wctx.Value when w.value is NilVal (FormatDisplay called
before Prepare), and handles unknown/dynamic values gracefully.

Previously, reconfigure showed "<not set>" for bundle-ref inputs
because the stored value was a resolved object, not a raw string.
Bundle-ref inputs are stored as resolved objects (from tm_bundle)
but must be written as alias strings in YAML and handled correctly
during type validation.

- BundleType.Apply: accept resolved objects, extract alias string
- generateBundleYAML: unwrap bundle-typed values to aliases using
  def.Type check before YAML serialization
- BundleRefWidget.FormatDisplay: handle object values, extract alias
  for display (fixes "<not set>" in reconfigure)
- Sort groups alphabetically by name, instances by alias
- Sort promote bundles in grouped display order to fix cursor mismatch
…n promote

When promoting a bundle that references another bundle not yet
promoted to the target environment, show a clear message:
"A referenced bundle has not been promoted to this environment
yet. Promote dependencies first."
- Keyboard shortcuts only match when focus is on commands (move
  focus check into case condition to avoid commandIdx side-effects)
- Bounds check flatBundleCursor before accessing flatBundles
- Use rune-based truncation for correct multi-byte character handling
- Improve wrapMissingBundleRefError to match "This value is null"
  (the actual HCL error text) instead of generic "null"
- Remove view_env_select.go (unreachable — no code path sets ViewEnvSelect)
- Remove ViewEnvSelect dispatch cases from Update/View
- Remove unused envCursor field from Model
- Inline nextViewAfterCloudLogin (trivial one-liner) and remove function
Use lipgloss.Width() instead of len() for label padding calculation
to correctly handle multi-byte characters.
The parameter was used for right-aligning the env label which was
removed earlier. All 10 callers updated.
loadBundleDef can fail for multiple reasons (resolve, load, parse,
enablement). Using "Bundle is not enabled" as the title for all
failures was misleading. Now uses the generic "Error" title.
Removed unused updateErrorWithTitle function.
…c order

Groups with the same name but different version/source now sort
deterministically by using the detail string (version+source)
as a secondary sort key.
Previously, Ctrl+C with an error dialog visible would set
ctrlCPending but the dialog stayed. The second Ctrl+C would
dismiss the dialog instead of exiting. Now Ctrl+C always
clears the dialog first, so the double-press exit works
regardless of dialog state.
@mariux mariux marked this pull request as ready for review April 12, 2026 21:38
@mariux mariux requested a review from a team as a code owner April 12, 2026 21:38
Comment thread commands/ui/view_reconfig.go
@mariux mariux force-pushed the worktree-more-ui-changes branch from 7f3d5be to e676594 Compare April 12, 2026 21:45
Comment thread commands/ui/inputs_form.go
@github-actions

github-actions Bot commented Apr 12, 2026

Copy link
Copy Markdown
## Package: benchmarks/changed
metric: time/op
ChangeDetection-4: old 1.41ms ± 8%: new 1.29ms ± 9%: delta: -8.46%
ChangeDetectionTFAndTG-4: old 1.26ms ±11%: new 1.21ms ± 7%: delta: 0.00%
metric: alloc/op
ChangeDetection-4: old 356kB ± 0%: new 356kB ± 0%: delta: 0.00%
ChangeDetectionTFAndTG-4: old 247kB ± 0%: new 247kB ± 0%: delta: 0.00%
metric: allocs/op
ChangeDetection-4: old 2.40k ± 0%: new 2.40k ± 0%: delta: 0.00%
ChangeDetectionTFAndTG-4: old 1.66k ± 1%: new 1.67k ± 1%: delta: 0.00%

---

## Package: cloud
metric: time/op
CloudReadLines-4: old 704µs ±14%: new 582µs ± 5%: delta: -17.30%
CloudReadLine-4: old 3.30ms ±14%: new 3.23ms ± 6%: delta: 0.00%
metric: alloc/op
CloudReadLines-4: old 3.12MB ± 0%: new 3.12MB ± 0%: delta: 0.00%
CloudReadLine-4: old 3.37MB ± 0%: new 3.37MB ± 0%: delta: 0.00%
metric: allocs/op
CloudReadLines-4: old 5.54k ± 0%: new 5.54k ± 0%: delta: 0.00%
CloudReadLine-4: old 60.0k ± 0%: new 60.0k ± 0%: delta: 0.00%

---

## Package: fs
metric: time/op
ListFiles-4: old 24.9µs ± 4%: new 25.8µs ± 5%: delta: 3.94%
metric: alloc/op
ListFiles-4: old 27.6kB ± 0%: new 27.6kB ± 0%: delta: 0.00%
metric: allocs/op
ListFiles-4: old 335 ± 0%: new 335 ± 0%: delta: 0.00%

---

## Package: generate
metric: time/op
Generate-4: old 687ms ± 9%: new 683ms ±10%: delta: 0.00%
GenerateRegex-4: old 390ms ±11%: new 419ms ± 4%: delta: 7.43%
metric: alloc/op
Generate-4: old 2.23GB ± 0%: new 2.23GB ± 0%: delta: 0.00%
GenerateRegex-4: old 922MB ± 0%: new 922MB ± 0%: delta: 0.00%
metric: allocs/op
Generate-4: old 25.7M ± 0%: new 25.7M ± 0%: delta: 0.00%
GenerateRegex-4: old 18.1M ± 0%: new 18.1M ± 0%: delta: 0.00%

---

## Package: hcl/ast
metric: time/op
TokensForExpressionComplex-4: old 805µs ±10%: new 855µs ±10%: delta: 6.16%
TokensForExpressionPlainStringNoNewline-4: old 376ns ±30%: new 393ns ±15%: delta: 0.00%
TokensForExpressionStringWith100Newlines-4: old 10.7µs ±19%: new 11.0µs ±13%: delta: 0.00%
TokensForExpressionObjectWith100KeysWithNumberValues-4: old 958µs ± 9%: new 980µs ± 8%: delta: 0.00%
TokensForExpression-4: old 809µs ±11%: new 822µs ± 6%: delta: 0.00%
metric: alloc/op
TokensForExpressionComplex-4: old 393kB ± 0%: new 393kB ± 0%: delta: 0.01%
TokensForExpressionPlainStringNoNewline-4: old 512B ± 0%: new 512B ± 0%: delta: 0.00%
TokensForExpressionStringWith100Newlines-4: old 12.5kB ± 0%: new 12.5kB ± 0%: delta: 0.00%
TokensForExpressionObjectWith100KeysWithNumberValues-4: old 393kB ± 0%: new 393kB ± 0%: delta: 0.00%
TokensForExpression-4: old 393kB ± 0%: new 393kB ± 0%: delta: 0.00%
metric: allocs/op
TokensForExpressionComplex-4: old 4.75k ± 0%: new 4.75k ± 0%: delta: 0.00%
TokensForExpressionPlainStringNoNewline-4: old 20.0 ± 0%: new 20.0 ± 0%: delta: 0.00%
TokensForExpressionStringWith100Newlines-4: old 227 ± 0%: new 227 ± 0%: delta: 0.00%
TokensForExpressionObjectWith100KeysWithNumberValues-4: old 3.09k ± 0%: new 3.09k ± 0%: delta: 0.00%
TokensForExpression-4: old 4.75k ± 0%: new 4.75k ± 0%: delta: 0.00%

---

## Package: hcl/eval
metric: time/op
PartialEvalComplex-4: old 238µs ± 7%: new 251µs ± 3%: delta: 5.64%
PartialEvalSmallString-4: old 1.65µs ± 9%: new 1.62µs ± 3%: delta: 0.00%
PartialEvalHugeString-4: old 845µs ± 6%: new 832µs ± 3%: delta: 0.00%
PartialEvalHugeInterpolatedString-4: old 1.92ms ± 9%: new 2.19ms ±14%: delta: 13.54%
PartialEvalObject-4: old 11.4µs ± 9%: new 12.3µs ± 8%: delta: 7.73%
metric: alloc/op
PartialEvalComplex-4: old 360kB ± 0%: new 360kB ± 0%: delta: 0.00%
PartialEvalSmallString-4: old 1.94kB ± 0%: new 1.94kB ± 0%: delta: 0.00%
PartialEvalHugeString-4: old 196kB ± 0%: new 196kB ± 0%: delta: 0.00%
PartialEvalHugeInterpolatedString-4: old 4.30MB ± 0%: new 4.30MB ± 0%: delta: -0.00%
PartialEvalObject-4: old 19.4kB ± 0%: new 19.4kB ± 0%: delta: 0.00%
metric: allocs/op
PartialEvalComplex-4: old 3.72k ± 0%: new 3.72k ± 0%: delta: 0.00%
PartialEvalSmallString-4: old 25.0 ± 0%: new 25.0 ± 0%: delta: 0.00%
PartialEvalHugeString-4: old 38.0 ± 0%: new 38.0 ± 0%: delta: 0.00%
PartialEvalHugeInterpolatedString-4: old 25.1k ± 0%: new 25.1k ± 0%: delta: 0.00%
PartialEvalObject-4: old 179 ± 0%: new 179 ± 0%: delta: 0.00%

---

## Package: ls
metric: time/op
FindDefinition/10_files-4: old 14.9µs ±17%: new 17.9µs ±20%: delta: 20.15%
FindDefinition/50_files-4: old 21.3µs ±10%: new 23.8µs ±19%: delta: 11.80%
FindDefinition/100_files-4: old 30.8µs ±10%: new 35.3µs ±15%: delta: 14.73%
FindDefinition/500_files-4: old 113µs ± 6%: new 141µs ±15%: delta: 24.65%
FindReferences/10_files_5_refs-4: old 87.8µs ±21%: new 94.7µs ±28%: delta: 7.82%
FindReferences/50_files_10_refs-4: old 376µs ±25%: new 317µs ± 7%: delta: -15.77%
FindReferences/100_files_20_refs-4: old 788µs ±23%: new 599µs ±10%: delta: -23.99%
FindReferences/500_files_50_refs-4: old 3.61ms ±24%: new 2.99ms ± 9%: delta: -17.09%
Rename/10_files_5_refs-4: old 100µs ±14%: new 98µs ±10%: delta: 0.00%
Rename/50_files_10_refs-4: old 340µs ± 8%: new 321µs ±10%: delta: -5.65%
Rename/100_files_20_refs-4: old 688µs ±18%: new 647µs ± 8%: delta: -6.02%
SearchWorkspace/flat_100_files-4: old 663µs ±15%: new 600µs ±24%: delta: -9.59%
SearchWorkspace/flat_500_files-4: old 3.00ms ±12%: new 3.36ms ±11%: delta: 11.89%
SearchWorkspace/nested_100_files_5_deep-4: old 600µs ± 6%: new 697µs ± 8%: delta: 16.19%
SearchWorkspace/nested_500_files_5_deep-4: old 2.82ms ±10%: new 3.41ms ±15%: delta: 20.82%
ParseFile/small_file_10_lines-4: old 20.5µs ±12%: new 20.8µs ± 8%: delta: 0.00%
ParseFile/medium_file_50_lines-4: old 100µs ±14%: new 92µs ± 6%: delta: -7.93%
ParseFile/large_file_200_lines-4: old 459µs ±19%: new 371µs ± 8%: delta: -19.13%
ParseFile/huge_file_1000_lines-4: old 2.30ms ±15%: new 1.85ms ± 4%: delta: -19.48%
check failed: time/op=+20%
metric: alloc/op
FindDefinition/10_files-4: old 15.2kB ± 0%: new 15.2kB ± 0%: delta: 0.02%
FindDefinition/50_files-4: old 20.1kB ± 0%: new 20.1kB ± 0%: delta: 0.01%
FindDefinition/100_files-4: old 26.4kB ± 0%: new 26.4kB ± 0%: delta: 0.01%
FindDefinition/500_files-4: old 72.8kB ± 0%: new 72.8kB ± 0%: delta: 0.01%
FindReferences/10_files_5_refs-4: old 87.0kB ± 0%: new 87.0kB ± 0%: delta: 0.00%
FindReferences/50_files_10_refs-4: old 336kB ± 0%: new 336kB ± 0%: delta: -0.03%
FindReferences/100_files_20_refs-4: old 646kB ± 0%: new 646kB ± 0%: delta: -0.02%
FindReferences/500_files_50_refs-4: old 3.13MB ± 0%: new 3.13MB ± 0%: delta: -0.02%
Rename/10_files_5_refs-4: old 105kB ± 0%: new 104kB ± 0%: delta: -0.05%
Rename/50_files_10_refs-4: old 359kB ± 0%: new 359kB ± 0%: delta: -0.01%
Rename/100_files_20_refs-4: old 677kB ± 0%: new 677kB ± 0%: delta: 0.00%
SearchWorkspace/flat_100_files-4: old 628kB ± 0%: new 628kB ± 0%: delta: 0.00%
SearchWorkspace/flat_500_files-4: old 3.09MB ± 0%: new 3.09MB ± 0%: delta: 0.00%
SearchWorkspace/nested_100_files_5_deep-4: old 637kB ± 0%: new 637kB ± 0%: delta: 0.00%
SearchWorkspace/nested_500_files_5_deep-4: old 3.13MB ± 0%: new 3.13MB ± 0%: delta: 0.01%
ParseFile/small_file_10_lines-4: old 34.7kB ± 0%: new 34.7kB ± 0%: delta: 0.00%
ParseFile/medium_file_50_lines-4: old 151kB ± 0%: new 151kB ± 0%: delta: 0.00%
ParseFile/large_file_200_lines-4: old 733kB ± 0%: new 733kB ± 0%: delta: 0.00%
ParseFile/huge_file_1000_lines-4: old 4.06MB ± 0%: new 4.06MB ± 0%: delta: 0.00%
metric: allocs/op
FindDefinition/10_files-4: old 209 ± 0%: new 209 ± 0%: delta: 0.00%
FindDefinition/50_files-4: old 291 ± 0%: new 291 ± 0%: delta: 0.00%
FindDefinition/100_files-4: old 392 ± 0%: new 392 ± 0%: delta: 0.00%
FindDefinition/500_files-4: old 1.19k ± 0%: new 1.19k ± 0%: delta: 0.00%
FindReferences/10_files_5_refs-4: old 960 ± 0%: new 960 ± 0%: delta: 0.00%
FindReferences/50_files_10_refs-4: old 3.81k ± 0%: new 3.81k ± 0%: delta: 0.00%
FindReferences/100_files_20_refs-4: old 7.34k ± 0%: new 7.34k ± 0%: delta: 0.00%
FindReferences/500_files_50_refs-4: old 35.9k ± 0%: new 35.9k ± 0%: delta: 0.00%
Rename/10_files_5_refs-4: old 1.15k ± 0%: new 1.15k ± 0%: delta: 0.00%
Rename/50_files_10_refs-4: old 4.09k ± 0%: new 4.09k ± 0%: delta: 0.00%
Rename/100_files_20_refs-4: old 7.74k ± 0%: new 7.74k ± 0%: delta: 0.00%
SearchWorkspace/flat_100_files-4: old 7.13k ± 0%: new 7.13k ± 0%: delta: 0.00%
SearchWorkspace/flat_500_files-4: old 35.1k ± 0%: new 35.1k ± 0%: delta: 0.00%
SearchWorkspace/nested_100_files_5_deep-4: old 7.18k ± 0%: new 7.18k ± 0%: delta: 0.00%
SearchWorkspace/nested_500_files_5_deep-4: old 35.2k ± 0%: new 35.2k ± 0%: delta: 0.00%
ParseFile/small_file_10_lines-4: old 315 ± 0%: new 315 ± 0%: delta: 0.00%
ParseFile/medium_file_50_lines-4: old 1.39k ± 0%: new 1.39k ± 0%: delta: 0.00%
ParseFile/large_file_200_lines-4: old 5.46k ± 0%: new 5.46k ± 0%: delta: 0.00%
ParseFile/huge_file_1000_lines-4: old 26.2k ± 0%: new 26.2k ± 0%: delta: 0.00%

---

## Package: stdlib
metric: time/op
TmAllTrueLiteralList-4: old 256µs ± 7%: new 255µs ± 6%: delta: 0.00%
TmAllTrueFuncall-4: old 8.11µs ± 3%: new 8.46µs ± 4%: delta: 4.28%
TmAnyTrueLiteralList-4: old 1.99ms ± 1%: new 2.01ms ± 8%: delta: 0.00%
TmAnyTrueFuncall-4: old 8.40µs ± 1%: new 8.78µs ±12%: delta: 4.56%
TmTernary-4: old 1.04µs ± 3%: new 1.04µs ± 9%: delta: 0.00%
TmTryUnknownFunc-4: old 955ns ± 2%: new 1045ns ±17%: delta: 9.40%
TmTryUnknownVariable-4: old 912ns ± 1%: new 1664ns ±77%: delta: 82.40%
TmTryUnknownObjectKey-4: old 1.05µs ± 2%: new 2.45µs ±12%: delta: 132.64%
SlugifyLargeList-4: old 989µs ± 2%: new 1381µs ±58%: delta: 39.66%
check failed: time/op=+20%
metric: alloc/op
TmAllTrueLiteralList-4: old 320kB ± 0%: new 320kB ± 0%: delta: 0.00%
TmAllTrueFuncall-4: old 10.4kB ± 0%: new 10.4kB ± 0%: delta: 0.00%
TmAnyTrueLiteralList-4: old 2.09MB ± 0%: new 2.09MB ± 0%: delta: 0.00%
TmAnyTrueFuncall-4: old 10.5kB ± 0%: new 10.5kB ± 0%: delta: 0.00%
TmTernary-4: old 1.18kB ± 0%: new 1.18kB ± 0%: delta: 0.00%
TmTryUnknownFunc-4: old 784B ± 0%: new 784B ± 0%: delta: 0.00%
TmTryUnknownVariable-4: old 768B ± 0%: new 768B ± 0%: delta: 0.00%
TmTryUnknownObjectKey-4: old 952B ± 0%: new 952B ± 0%: delta: 0.00%
SlugifyLargeList-4: old 613kB ± 0%: new 614kB ± 0%: delta: 0.07%
metric: allocs/op
TmAllTrueLiteralList-4: old 5.93k ± 0%: new 5.93k ± 0%: delta: 0.00%
TmAllTrueFuncall-4: old 265 ± 0%: new 265 ± 0%: delta: 0.00%
TmAnyTrueLiteralList-4: old 59.6k ± 0%: new 59.6k ± 0%: delta: 0.00%
TmAnyTrueFuncall-4: old 267 ± 0%: new 267 ± 0%: delta: 0.00%
TmTernary-4: old 27.0 ± 0%: new 27.0 ± 0%: delta: 0.00%
TmTryUnknownFunc-4: old 21.0 ± 0%: new 21.0 ± 0%: delta: 0.00%
TmTryUnknownVariable-4: old 20.0 ± 0%: new 20.0 ± 0%: delta: 0.00%
TmTryUnknownObjectKey-4: old 23.0 ± 0%: new 23.0 ± 0%: delta: 0.00%
SlugifyLargeList-4: old 18.0k ± 0%: new 18.0k ± 0%: delta: 0.00%

---

## Package: tg
metric: time/op
ModuleDiscovery-4: old 31.0ms ± 2%: new 31.2ms ± 3%: delta: 0.00%
metric: alloc/op
ModuleDiscovery-4: old 36.8MB ± 0%: new 36.8MB ± 0%: delta: 0.00%
metric: allocs/op
ModuleDiscovery-4: old 451k ± 0%: new 451k ± 0%: delta: 0.00%

---

Comment thread commands/ui/view_promote.go
@github-actions

github-actions Bot commented Apr 12, 2026

Copy link
Copy Markdown

Preview of macos-15/go tests in 0a8f97f

🔍 View Details on Terramate Cloud

.
commands/ui

@mariux mariux force-pushed the worktree-more-ui-changes branch 2 times, most recently from c8c0dbf to 2c1328a Compare April 12, 2026 23:45
Comment thread commands/ui/view_reconfig.go
Comment thread commands/ui/view_overview.go Outdated
@mariux mariux force-pushed the worktree-more-ui-changes branch from 2c1328a to f72d46f Compare April 13, 2026 00:01
Comment thread commands/ui/view_reconfig.go
@mariux mariux force-pushed the worktree-more-ui-changes branch from f72d46f to 4099cf0 Compare April 13, 2026 00:18
Comment thread commands/ui/view_promote.go
- Reconfigure and Promote now save immediately on confirm (like Create)
- Remove Edit flow (view_edit.go deleted) — no pending changes to edit
- Remove PendingChanges/ProposedChanges infrastructure from Registry
- Replace pending summary panel with "Recently Changed" session panel
  showing grouped bundles with change kind tags (created/reconfigured/promoted)
- Session panel allows selecting a bundle to reconfigure it again
- Reconfig/promote input form: single unified list (no Changed/Unchanged split)
- Immutable inputs: blue values, "immutable" tag, cursor skips them
- Changed values shown in orange with inline diff (new <- old)
- Buttons at bottom: Save/Cancel (or Back when no changes in reconfig)
- ESC with unsaved reconfig changes triggers discard confirmation
- After editing, cursor returns to the just-edited input
- Rebind bundle()/bundles() in NewReconfigChange and NewPromoteChange
- Remove dead code: SavedChange, NewChangeFromExisting, MarkedForReplacement,
  ProposalID, reEditing, hasEdited, skipDiscardConfirm, cancelLabel,
  savedCompletedCursor, completedVisibleIndices, completedCursorToLine

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mariux mariux force-pushed the worktree-more-ui-changes branch from 4099cf0 to d9152b1 Compare April 13, 2026 00:56
Comment thread commands/ui/change.go Outdated
Comment thread commands/ui/change.go
mariux and others added 2 commits April 13, 2026 12:17
- [Without Environment] tag uses same blue color as other env tags
- Left/right arrow nav cycles between Save/Cancel buttons
- Last saved bundle highlighted with bold + "saved" tag (clears on keypress)
- Command hotkeys use underlined letter instead of [c] suffix
- Hotkeys accept both upper and lowercase input

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove ui.Registry wrapper — use *config.Registry directly
- MatchingBundleOptions and IsBundleUnique are now standalone functions
- Replace context.TODO() with est.Context in all change constructors
- Remove double-dereference (est.Registry.Registry → est.Registry)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mariux mariux changed the base branch from worktree-ui-changes to main April 13, 2026 10:40
@mariux mariux changed the title refactor(ui): remove pending changes, save all actions immediately refactor(ui): action-first flow with immediate save Apr 13, 2026
- Remove object→alias extraction from BundleType.Apply (typeschema layer)
- Add normalizeBundleRefValues() in UI layer to extract aliases before
  values enter the form
- Replace brittle string-matching wrapMissingBundleRefError with
  checkBundleRefsResolved() that checks for null bundle-ref values
  after evaluation — produces a clear error with the input name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread commands/ui/view_reconfig.go Outdated
Comment thread commands/ui/view_promote.go
Comment thread commands/ui/model.go
Comment thread commands/ui/inputs_form.go
- Hoist currentReconfigFilter() call outside the per-bundle loop
- Only switch to InputFocusCompleted after confirm when all inputs
  are still filled (clearDependents may create unfilled inputs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread commands/ui/view_create_select.go
Comment thread commands/ui/change.go
… all flows

- Add bounds check on LocalBundleDefs[bundleIdx] access to prevent
  potential out-of-bounds panic if collections and local defs diverge
- Add checkBundleRefsResolved to NewCreateChange and NewReconfigChange
  (was only in NewPromoteChange) for consistent bundle-ref validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread commands/ui/view_reconfig.go Outdated
Comment thread commands/ui/view_promote.go
- Reconfig discard: goes to ViewReconfigSelect (or ViewOverview if
  entered from session panel)
- Promote discard: goes to ViewPromoteSelect

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 4 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2f7199e. Configure here.

Comment thread commands/ui/view_create.go
Comment thread commands/ui/view_create_select.go
After reloadAll(), parent form widgets hold the old *config.Registry
pointer via SharedWidgetContext. Replace content in-place (*est.Registry
= *newReg) so all existing pointers see the updated data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@snakster snakster left a comment

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.

👍

@mariux mariux merged commit aeaf3a4 into main Apr 13, 2026
15 checks passed
@mariux mariux deleted the worktree-more-ui-changes branch April 13, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants