From f9fca266d1bf009d3f3b74e6d29b56e90242429d Mon Sep 17 00:00:00 2001 From: Ashlee Radka Date: Wed, 22 Apr 2026 16:31:31 -0400 Subject: [PATCH] ci(macos): lint rule for .frame(maxWidth:) / .frame(maxHeight:) regressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a fast ripgrep-based guard that fails CI when a new `.frame(maxWidth:)` or `.frame(maxHeight:)` is introduced inside `Features/Chat/` or `Features/MainWindow/`. These modifiers create `_FlexFrameLayout`, which cascades `explicitAlignment` queries through descendants and has caused multi-second hangs in LazyVStack-backed chat surfaces 9+ times (PRs #24019, #24091, #24584, #24589, #25844, #25947, #26007, #26053, #26092, #26220). The manual audit process missed regressions twice — this lint enforces the AGENTS.md:277-286 rule mechanically. Tracked in LUM-1116. Content-hash allowlist (`clients/scripts/flexframe-allowlist.txt`) seeded with the 170 existing occurrences so the check passes on current main. Entries are keyed on `|` so unrelated line drift doesn't break them. --- .github/workflows/ci-main-macos.yaml | 21 +++ .github/workflows/pr-macos.yaml | 21 +++ clients/scripts/check-flexframe.sh | 241 ++++++++++++++++++++++++ clients/scripts/flexframe-allowlist.txt | 198 +++++++++++++++++++ 4 files changed, 481 insertions(+) create mode 100755 clients/scripts/check-flexframe.sh create mode 100644 clients/scripts/flexframe-allowlist.txt diff --git a/.github/workflows/ci-main-macos.yaml b/.github/workflows/ci-main-macos.yaml index 85867305014..8d162279a5f 100644 --- a/.github/workflows/ci-main-macos.yaml +++ b/.github/workflows/ci-main-macos.yaml @@ -41,6 +41,27 @@ jobs: - 'clients/**' - '.github/workflows/ci-main-macos.yaml' + flexframe-lint: + name: FlexFrame Lint + needs: [changes] + if: needs.changes.outputs.clients == 'true' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Ensure ripgrep is available + run: | + if ! command -v rg >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y ripgrep + fi + rg --version | head -1 + + - name: FlexFrame guard + run: bash clients/scripts/check-flexframe.sh + test: name: macOS Tests needs: [changes] diff --git a/.github/workflows/pr-macos.yaml b/.github/workflows/pr-macos.yaml index b4c78105901..65e2d29a308 100644 --- a/.github/workflows/pr-macos.yaml +++ b/.github/workflows/pr-macos.yaml @@ -10,6 +10,8 @@ on: - 'clients/.periphery.yml' - 'clients/.periphery_baseline.json' - 'clients/scripts/periphery-scan.sh' + - 'clients/scripts/check-flexframe.sh' + - 'clients/scripts/flexframe-allowlist.txt' - 'assistant/**' - 'gateway/**' - 'credential-executor/**' @@ -21,6 +23,25 @@ concurrency: cancel-in-progress: true jobs: + flexframe-lint: + name: FlexFrame Lint + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Ensure ripgrep is available + run: | + if ! command -v rg >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y ripgrep + fi + rg --version | head -1 + + - name: FlexFrame guard + run: bash clients/scripts/check-flexframe.sh + lint-unused: if: contains(github.event.pull_request.labels.*.name, 'preview') name: Lint Unused Code diff --git a/clients/scripts/check-flexframe.sh b/clients/scripts/check-flexframe.sh new file mode 100755 index 00000000000..159a12d0e28 --- /dev/null +++ b/clients/scripts/check-flexframe.sh @@ -0,0 +1,241 @@ +#!/usr/bin/env bash +set -euo pipefail + +# FlexFrame guardrail script +# +# Detects `.frame(maxWidth:)` / `.frame(maxHeight:)` usages in performance-sensitive +# chat/window directories. These modifiers create `_FlexFrameLayout`, which queries +# `explicitAlignment` on descendants — cascading O(depth × children) per layout pass +# and causing multi-second hangs in LazyVStack-backed hierarchies. +# +# See clients/macos/AGENTS.md (section "No `.frame(maxWidth:)` ... in LazyVStack/ +# LazyHStack/LazyVGrid cell hierarchy") for the rule and safe alternatives. +# +# Safe alternatives: +# - .widthCap(N) — O(1) width cap via WidthCapLayout +# - .frame(width: N) — _FrameLayout, no alignment query +# - HStack { content; Spacer(minLength: 0) } / Spacer + content — alignment without FlexFrame +# - BottomAlignedMinHeightLayout — vertical equivalent +# +# Historical context: this cascade has been fixed 9+ times in chat-surface code +# (PRs #24019, #24091, #24584, #24589, #25844, #25947, #26007, #26053, #26092, #26220). +# The manual audit process missed regressions twice — this lint enforces the rule +# mechanically. Tracked in LUM-1116. +# +# Usage: check-flexframe.sh [--update-baseline] +# +# Baseline (allowlist) format — `clients/scripts/flexframe-allowlist.txt`: +# | +# One entry per occurrence (multiplicity-preserving). Line numbers are intentionally +# NOT part of the key so the allowlist survives unrelated line drift. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CLIENTS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REPO_ROOT="$(cd "$CLIENTS_DIR/.." && pwd)" +ALLOWLIST_FILE="$SCRIPT_DIR/flexframe-allowlist.txt" + +UPDATE_BASELINE=0 +for arg in "$@"; do + case "$arg" in + --update-baseline) UPDATE_BASELINE=1 ;; + -h|--help) + sed -n '2,25p' "$0" | sed 's/^# \?//' + exit 0 ;; + *) echo "Unknown argument: $arg" >&2; exit 1 ;; + esac +done + +# Scope: performance-sensitive chat + main window feature directories. +# Conservative by design; expand in a follow-up if this proves valuable. +SCAN_DIRS=( + "clients/macos/vellum-assistant/Features/Chat/" + "clients/macos/vellum-assistant/Features/MainWindow/" +) + +# Matches .frame(maxWidth: ...) or .frame(maxHeight: ...) — any value. +# Rust-regex compatible (no lookaround) so it works with ripgrep's default +# engine; we strip comment-only lines in a second pass below. +PATTERN='\.frame\(\s*max(Width|Height)\s*:' + +cd "$REPO_ROOT" + +if ! command -v rg >/dev/null 2>&1; then + echo "ERROR: ripgrep (rg) is required but not found in PATH." >&2 + echo " macOS: brew install ripgrep" >&2 + echo " Ubuntu: apt-get install ripgrep" >&2 + exit 2 +fi + +# Collect raw hits with line numbers, then drop comment-only lines +# (lines whose first non-whitespace is `//` or `///`). AGENTS.md-style +# warnings like `// ⚠️ No .frame(maxWidth:) in LazyVStack cells` would +# otherwise false-positive. +RAW_HITS=$(rg -n --no-heading "$PATTERN" "${SCAN_DIRS[@]}" 2>/dev/null \ + | grep -vE '^[^:]+:[0-9]+:[[:space:]]*//' \ + || true) + +# Build the comparison set: `|` (no line number, whitespace stripped). +# Preserves multiplicity via plain `sort` (not `sort -u`). +normalize() { + # Input: clients/.../Foo.swift:42: .frame(maxWidth: .infinity) + # Output: clients/.../Foo.swift|.frame(maxWidth: .infinity) + sed -E 's/^([^:]+):[0-9]+:[[:space:]]*/\1|/' +} + +OBSERVED_NORMALIZED="" +if [[ -n "$RAW_HITS" ]]; then + OBSERVED_NORMALIZED=$(printf '%s\n' "$RAW_HITS" | normalize | sort) +fi + +# --update-baseline: rewrite the allowlist to match the current observed set. +# Use sparingly; every entry is a TODO to eventually convert to a safe alternative. +if [[ "$UPDATE_BASELINE" == "1" ]]; then + { + cat <<'HEADER' +# FlexFrame allowlist — intentional `.frame(maxWidth:)` / `.frame(maxHeight:)` usages. +# +# Each line is `|` for one occurrence. Line numbers +# are intentionally omitted so entries survive unrelated line drift. +# +# Why an entry is here (typical reasons): +# - Leaf view (Text / Image / VIconView) where `_FlexFrameLayout`'s cascade +# bottoms out immediately — cost is O(0), so the alignment-query concern +# is purely theoretical. (e.g. `.frame(maxWidth: .infinity, alignment: .leading)` +# wrapping a single `Text` with `.lineLimit(1).truncationMode(.tail)` — a +# configuration that `HStack + Spacer` breaks.) +# - Top-level container outside any Lazy* hierarchy where an explicit +# fill-parent semantic is load-bearing. +# - Sheet / modal / detail panel surfaces rendered eagerly (no lazy container +# and no animated transition in the parent). +# +# Adding a new entry: BEFORE allowlisting, first try a safe alternative: +# .widthCap(N), .frame(width: N), HStack+Spacer, BottomAlignedMinHeightLayout. +# If and only if none of those preserve required semantics (truncation, exact +# alignment, fill-parent for a modal root), add the entry and a one-line note +# in the PR description explaining why. The default answer is "use a safe +# alternative"; this file is a last resort, not a general escape hatch. +# +# Regenerate this file after an intentional bulk refactor with: +# bash clients/scripts/check-flexframe.sh --update-baseline +# +# See clients/macos/AGENTS.md §§ "No `.frame(maxWidth:)` ... in LazyVStack/ +# LazyHStack/LazyVGrid cell hierarchy" for the underlying rule. +HEADER + if [[ -n "$OBSERVED_NORMALIZED" ]]; then + printf '%s\n' "$OBSERVED_NORMALIZED" + fi + } > "$ALLOWLIST_FILE" + COUNT=$(printf '%s\n' "$OBSERVED_NORMALIZED" | grep -cE '.' || true) + echo "Wrote $COUNT allowlist entries to $ALLOWLIST_FILE" + exit 0 +fi + +# Load the allowlist (strip comments + blank lines), preserving multiplicity. +ALLOWLIST_ENTRIES="" +if [[ -f "$ALLOWLIST_FILE" ]]; then + ALLOWLIST_ENTRIES=$(grep -vE '^([[:space:]]*#|[[:space:]]*$)' "$ALLOWLIST_FILE" | sort) +fi + +# New violations = observed - allowlist (multiset difference preserved by `comm -23`). +NEW_NORMALIZED="" +if [[ -n "$OBSERVED_NORMALIZED" ]]; then + NEW_NORMALIZED=$(comm -23 \ + <(printf '%s\n' "$OBSERVED_NORMALIZED") \ + <(printf '%s\n' "$ALLOWLIST_ENTRIES")) +fi + +# Stale allowlist entries = allowlist - observed. Warn (don't fail) so the +# allowlist shrinks as code is cleaned up. +STALE_NORMALIZED="" +if [[ -n "$ALLOWLIST_ENTRIES" ]]; then + STALE_NORMALIZED=$(comm -13 \ + <(printf '%s\n' "${OBSERVED_NORMALIZED:-}") \ + <(printf '%s\n' "$ALLOWLIST_ENTRIES")) +fi + +# Count non-blank lines. `grep -c` handles trailing-newline edge cases correctly +# (unlike `printf '%s' | wc -l` which undercounts by 1 when there's no final \n). +count_nonblank() { + if [[ -z "${1:-}" ]]; then echo 0; return; fi + printf '%s\n' "$1" | grep -cE '.' +} + +NEW_COUNT=$(count_nonblank "${NEW_NORMALIZED:-}") +STALE_COUNT=$(count_nonblank "${STALE_NORMALIZED:-}") + +# Map each new normalized violation back to the raw `file:line:content` hits +# for a useful diagnostic. A given `|` may map to multiple +# raw lines; we want exactly `new_count[key]` of them printed per key. +# +# Implemented with two temp files to stay compatible with bash 3.2 (macOS +# default — no associative arrays). +print_new_violations() { + local budget_file sorted_budget_file + budget_file=$(mktemp) + sorted_budget_file=$(mktemp) + # shellcheck disable=SC2064 + trap "rm -f '$budget_file' '$sorted_budget_file'" RETURN + + # Build a "budget" of how many instances of each normalized key are NEW. + printf '%s\n' "$NEW_NORMALIZED" \ + | grep -vE '^$' \ + | sort \ + | uniq -c \ + | sed -E 's/^[[:space:]]*([0-9]+)[[:space:]]+/\1\t/' \ + > "$sorted_budget_file" || true + + # Walk raw hits in source order; for each raw line, look up its normalized + # key's remaining budget and emit if > 0, decrementing as we go. + while IFS= read -r raw; do + [[ -z "$raw" ]] && continue + normalized=$(printf '%s\n' "$raw" | normalize) + # Lookup current remaining budget for this key. + remaining=$(awk -F'\t' -v k="$normalized" '$2 == k { print $1; exit }' "$sorted_budget_file") + if [[ -n "$remaining" && "$remaining" -gt 0 ]]; then + echo " $raw" + # Decrement. + awk -F'\t' -v k="$normalized" 'BEGIN{OFS=FS} + $2 == k { $1 = $1 - 1 } + { print } + ' "$sorted_budget_file" > "$budget_file" + mv "$budget_file" "$sorted_budget_file" + fi + done <<< "$RAW_HITS" +} + +if [[ "$NEW_COUNT" -gt 0 ]]; then + echo "=== flexframe lint: $NEW_COUNT new violation(s) ===" + echo + echo " .frame(maxWidth:) / .frame(maxHeight:) create _FlexFrameLayout, which queries" + echo " explicitAlignment on descendants and cascades O(depth × children) per layout" + echo " pass. This causes multi-second hangs in LazyVStack-backed chat hierarchies." + echo + echo " Safe alternatives (see clients/macos/AGENTS.md §§ 'No .frame(maxWidth:) ...'):" + echo " .widthCap(N) — O(1) width cap" + echo " .frame(width: N) — _FrameLayout, no alignment query" + echo " HStack { content; Spacer(minLength: 0) } — leading alignment, no FlexFrame" + echo " HStack { Spacer(minLength: 0); content } — trailing alignment, no FlexFrame" + echo " BottomAlignedMinHeightLayout — vertical fill, no FlexFrame" + echo + echo " If none of the above preserve the required semantics (e.g. single-line Text" + echo " truncation, modal-root fill-parent), add an entry to:" + echo " clients/scripts/flexframe-allowlist.txt" + echo " and explain why in your PR description." + echo + echo " New violation(s):" + print_new_violations + echo + if [[ "$STALE_COUNT" -gt 0 ]]; then + echo " Note: $STALE_COUNT allowlist entry/entries no longer match any code" + echo " (likely from prior cleanup). Run to tidy:" + echo " bash clients/scripts/check-flexframe.sh --update-baseline" + fi + exit 1 +fi + +OBSERVED_COUNT=$(count_nonblank "${OBSERVED_NORMALIZED:-}") +echo "flexframe lint: OK ($OBSERVED_COUNT allowlisted, 0 new)" +if [[ "$STALE_COUNT" -gt 0 ]]; then + echo " Note: $STALE_COUNT stale allowlist entry/entries — run with --update-baseline to prune." +fi +exit 0 diff --git a/clients/scripts/flexframe-allowlist.txt b/clients/scripts/flexframe-allowlist.txt new file mode 100644 index 00000000000..74ca2c8643f --- /dev/null +++ b/clients/scripts/flexframe-allowlist.txt @@ -0,0 +1,198 @@ +# FlexFrame allowlist — intentional `.frame(maxWidth:)` / `.frame(maxHeight:)` usages. +# +# Each line is `|` for one occurrence. Line numbers +# are intentionally omitted so entries survive unrelated line drift. +# +# Why an entry is here (typical reasons): +# - Leaf view (Text / Image / VIconView) where `_FlexFrameLayout`'s cascade +# bottoms out immediately — cost is O(0), so the alignment-query concern +# is purely theoretical. (e.g. `.frame(maxWidth: .infinity, alignment: .leading)` +# wrapping a single `Text` with `.lineLimit(1).truncationMode(.tail)` — a +# configuration that `HStack + Spacer` breaks.) +# - Top-level container outside any Lazy* hierarchy where an explicit +# fill-parent semantic is load-bearing. +# - Sheet / modal / detail panel surfaces rendered eagerly (no lazy container +# and no animated transition in the parent). +# +# Adding a new entry: BEFORE allowlisting, first try a safe alternative: +# .widthCap(N), .frame(width: N), HStack+Spacer, BottomAlignedMinHeightLayout. +# If and only if none of those preserve required semantics (truncation, exact +# alignment, fill-parent for a modal root), add the entry and a one-line note +# in the PR description explaining why. The default answer is "use a safe +# alternative"; this file is a last resort, not a general escape hatch. +# +# Regenerate this file after an intentional bulk refactor with: +# bash clients/scripts/check-flexframe.sh --update-baseline +# +# See clients/macos/AGENTS.md §§ "No `.frame(maxWidth:)` ... in LazyVStack/ +# LazyHStack/LazyVGrid cell hierarchy" for the underlying rule. +clients/macos/vellum-assistant/Features/Chat/AssistantConnectionTimeoutView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/Chat/AssistantConnectionTimeoutView.swift|.frame(maxWidth: 380) +clients/macos/vellum-assistant/Features/Chat/ChatBubbleOverflowMenu.swift|.frame(maxWidth: 280) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: 400) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: VSpacing.chatBubbleMaxWidth) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: VSpacing.chatBubbleMaxWidth) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: VSpacing.chatBubbleMaxWidth) +clients/macos/vellum-assistant/Features/Chat/ChatEmptyStateView.swift|.frame(maxWidth: VSpacing.chatBubbleMaxWidth) +clients/macos/vellum-assistant/Features/Chat/ChatErrorToastView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/ChatErrorToastView.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/ChatLoadingSkeleton.swift|.frame(maxWidth: .infinity, alignment: .trailing) +clients/macos/vellum-assistant/Features/Chat/ChatLoadingSkeleton.swift|.frame(maxWidth: VSpacing.chatBubbleMaxWidth * 0.45, alignment: .trailing) +clients/macos/vellum-assistant/Features/Chat/ChatLoadingSkeleton.swift|.frame(maxWidth: VSpacing.chatBubbleMaxWidth * 0.65) +clients/macos/vellum-assistant/Features/Chat/ChatLoadingSkeleton.swift|.frame(maxWidth: VSpacing.chatBubbleMaxWidth, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/ChatLoadingSkeleton.swift|.frame(maxWidth: VSpacing.chatColumnMaxWidth, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/ChatView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/Chat/ChatView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) +clients/macos/vellum-assistant/Features/Chat/ChatView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) +clients/macos/vellum-assistant/Features/Chat/ChatView.swift|.frame(maxWidth: 320) +clients/macos/vellum-assistant/Features/Chat/ComposerAttachments.swift|.frame(maxWidth: 280) +clients/macos/vellum-assistant/Features/Chat/ComposerView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/ComposerView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/ComposerView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/ImageLightbox/ZoomableImageView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorMemoryTab.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorMemoryTab.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorMemoryTab.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorMemoryTab.swift|.frame(maxWidth: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorMemoryTab.swift|.frame(maxWidth: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorMemoryTab.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorOverviewTab.swift|.frame(maxWidth: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorOverviewTab.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPayloadView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPayloadView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPayloadView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPromptTab.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPromptTab.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPromptTab.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPromptTab.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPromptTab.swift|.frame(maxWidth: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorPromptTab.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorResponseTab.swift|.frame(maxWidth: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorResponseTab.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: .infinity, minHeight: 220, alignment: .topLeading) +clients/macos/vellum-assistant/Features/Chat/MessageInspectorView.swift|.frame(maxWidth: 220) +clients/macos/vellum-assistant/Features/Chat/PlatformURLMismatchView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/Chat/PlatformURLMismatchView.swift|.frame(maxWidth: 380) +clients/macos/vellum-assistant/Features/Chat/QueuedMessageRow.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/RecoveryModeBanner.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/RuleEditorModal.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/RuleEditorModal.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/Chat/RuleEditorModal.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/DaemonLoadingOverlay.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/MainWindowErrorOverlay.swift|.frame(maxWidth: .infinity, alignment: .center) +clients/macos/vellum-assistant/Features/MainWindow/MainWindowView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/MainWindowView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/MainWindowView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/MainWindow/PanelCoordinator.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/PanelCoordinator.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/PanelCoordinator.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/PanelCoordinator.swift|.frame(maxWidth: 280) +clients/macos/vellum-assistant/Features/MainWindow/Panels/AgentPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/AgentPanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/AgentPanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/AppsGridView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/AppsGridView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/AppsGridView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/AppsGridView.swift|.frame(maxWidth: maxContentWidth) +clients/macos/vellum-assistant/Features/MainWindow/Panels/ConstellationView.swift|.frame(maxWidth: 260, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/ConstellationView.swift|.frame(maxWidth: size * (isDiamond ? 0.62 : 0.82)) +clients/macos/vellum-assistant/Features/MainWindow/Panels/ConstellationView.swift|.frame(maxWidth: size * 0.85) +clients/macos/vellum-assistant/Features/MainWindow/Panels/ConstellationView.swift|.frame(maxWidth: size * 0.85) +clients/macos/vellum-assistant/Features/MainWindow/Panels/HighlightedTextView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/HighlightedTextView.swift|.frame(maxWidth: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/HighlightedTextView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IdentityPanel.swift|.frame(maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IdentityPanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IdentityPanel.swift|.frame(maxWidth: .infinity, alignment: .center) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IdentityPanel.swift|.frame(maxWidth: .infinity, alignment: .center) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IdentityPanel.swift|.frame(maxWidth: .infinity, alignment: .center) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IdentityPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IdentityPanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IntelligencePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IntelligencePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IntelligencePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IntelligencePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/IntelligencePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/JSONTreeView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/JSONTreeView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/JSONTreeView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: 900, alignment: .top) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: 900, alignment: .top) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: breakdownTableWidth, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/LogsAndUsagePanel.swift|.frame(maxWidth: breakdownTableWidth, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/MarkdownPreviewView.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/MemoriesPanel.swift|.frame(maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/MemoriesPanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/MemoriesPanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/MemoriesPanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/MemoryImportSheet.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/MemoryItemDetailSheet+Content.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SettingsPanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SettingsPanel.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SettingsPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SettingsPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SettingsPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SettingsPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SettingsPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SettingsPanel.swift|.frame(maxWidth: 900, alignment: .top) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SharedFileViewerComponents.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SharedFileViewerComponents.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SharedFileViewerComponents.swift|.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SkillDetailView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SkillDetailView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SkillDetailView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SkillDetailView.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SkillDetailView.swift|.frame(maxWidth: 800, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SubagentDetailPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SubagentDetailPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SubagentDetailPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SubagentDetailPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/SubagentDetailPanel.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Panels/WorkspacePanel.swift|.frame(maxWidth: .infinity, maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/ConversationSwitcherDrawer.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/EarnCreditsModal.swift|.frame(maxWidth: .infinity, alignment: .center) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/EarnCreditsModal.swift|.frame(maxWidth: .infinity, alignment: .center) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/EarnCreditsModal.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/EarnCreditsModal.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/EarnCreditsModal.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/EarnCreditsModal.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/EarnCreditsModal.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/SidebarConversationItem.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/SidebarSectionView.swift|.frame(maxWidth: .infinity, alignment: .leading) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/SidebarView.swift|.frame(maxHeight: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/Sidebar/SidebarView.swift|.frame(maxWidth: .infinity) +clients/macos/vellum-assistant/Features/MainWindow/ThreadWindow.swift|.frame(maxWidth: .infinity, maxHeight: .infinity)