Skip to content

Toast placement with a right drawer on windows under ~1100px: the fallback clears only the drawer chrome, so the create-success toast covers the record header's 编辑 / ⋯ for its whole life (residual of objectui#11685) #26418

Closed

Description

@objectstack-fleet

Filing gate ① a reproduced UX defect (class a, public door: creating a record). reach: local stack, objectui pin 6f629a7, 1024×768 viewport, zh-CN, 2026-10-27. After 新建 → 创建 on a record form, the record drawer opens (width 720) and the success toast 「家装项目创建成功」 sits at x 644–1000, y 69–123 for its whole life. That covers the record's 「编辑」 button (rect 858–934 × 93–129) and the ⋯ menu beside it. Measured with elementFromPoint: the toast is topmost (z-index 999999999).

Batch-of: 2 · 2026-10-27 follow-up to the 2026-10-25 desktop E2E (doubtful items re-measured). Filed at the maintainer's request: 「必要的合理的建议都可以立卡」 / 「有疑点的进一步确认」.

Business reason. Right after creating a record, the next thing a user does is often fix something: they reach for 编辑. objectui#11685 set out to keep the toast off "the record's own header with its Approve-style actions under it". On any window narrower than about 1,100 px (small laptops, split screens, a browser beside a chat app), it still lands there for ~4 s.

Reader who acts. The domain:ui seat: ui/packages/app-shell/src/chrome/ConsoleToaster.tsx (objectui was folded into this repository; objectstack#25854).

Where (measured on objectui pin 6f629a7, which includes objectui#11685's fix 267b539f; unchanged on objectstack main at ui/packages/app-shell/src/chrome/ConsoleToaster.tsx:97-101)

  • ConsoleToaster.tsx:99-101: fitsBeside = innerWidth − drawerWidth ≥ TOAST_WIDTH + 2·gap. Here 1024 − 720 = 304 < 356, so the fallback { top: headerBottom + VIEWPORT_GAP } applies.
  • headerBottom (:97) is the dialog's top-level block holding the aria-labelledby title: the drawer chrome (「家装项目 / 家装项目 的记录详情浮层。」), bottom = 45 px. The record header with 编辑 / ⋯ is below it (93–129), so the fallback clears the chrome and lands on the actions. That is the case the module comment says it prevents.

Expected

In the fallback, the toast clears the record header's action row too. For example: measure to the bottom of the first actions row inside the dialog, or place the toast at the bottom-right of the drawer, away from both. Add a test at a 1024 px window.

Dedupe

gh api search/issues, incl. closed, toast drawer: objectui 34 (0 open), objectstack 33 (QA runs / sweep).

  • objectui#11685 (closed; fix 267b539f in this pin, compare 267b539f...6f629a7310 = ahead) is the same goal. This is the narrow-window residual its fallback does not cover.
  • objectui#11723 (closed) is toast clickability under an open dialog, which is different.

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_017iXjUm1rujwDk7f3KT1Pkk (seat); implementing card thread session_01ADHkmQQBJvECrfY7vVyetV
    Account: os-steve
    Branch: claude/project-thread-z125nk (platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)
    Worktree: the card thread's own container
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/app-shell/src/chrome/ConsoleToaster.tsx (the fitsBeside fallback), its test (1024 px window with a 720 px drawer, plus a wide-window control and an ablation), and a root changeset. Out of bounds: ObjectView.tsx / objectView*.ts (#25992), RecordDetailView.tsx (#26402), the sidebar (seat 2), ui/packages/app-shell/package.json (#25922) (stop on breach; explain in the report). ⛔ ui/.github/** and the root .github/workflows/ci.yml are out of bounds (maintainer's word, ui-merge epic in flight). Changeset in the root .changeset/.
    Container & model: small, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a: renderer defect in ui/, the producing code is the cited ui file; no platform path covers it; reached through the public Console
    Thread-read: none
    Premise: holds (read-only premise check at origin/main, 2026-10-27T03:16Z; spot-checked by the seat)
    Serial constraints cleared: none: no other open PR touches ConsoleToaster.tsx

    Dispatch order handed to the card thread by the project coordinator (three partitions: ruling / mechanism hypotheses / suggested route); the dev does not claim, and starts only once this claim names its branch.

  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26418,
      "status": "done",
      "branch": "claude/project-thread-z125nk",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26426",
      "head": "e8590370",
      "session": "session_01ADHkmQQBJvECrfY7vVyetV",
      "premise_still_valid": true,
      "summary": "The objectui#11685 fallback in ConsoleToaster.tsx (no room for a toast beside an open right-edge drawer) now drops the toast below the drawer's first row of controls as well as its header: the topmost visible control outside the header, widened by every control overlapping it vertically, measured from offsetTop sums (transform-proof and scroll-independent). A drawer-subtree MutationObserver re-measures when the record header renders after the drawer opens. Chose measuring over the drawer's bottom-right corner because the record drawer's bottom-right is where InlineEditSaveBar's Save / Cancel sticks, and a failed save raises its toast exactly then; the corner option would also flip the toaster to a bottom anchor (objectui#7482). Wide-window placement (objectui#11685) unchanged.",
      "files_changed": [
        "ui/packages/app-shell/src/chrome/ConsoleToaster.tsx (+71 / -7)",
        "ui/packages/app-shell/src/chrome/ConsoleToaster.drawerActionsRow-26418.test.tsx (new, 197 lines)",
        ".changeset/26418-toast-clears-drawer-actions-row.md (new, @object-ui/app-shell patch)"
      ],
      "line_budget": "286 changed lines (+279 / -7) vs the 3000 human-merge threshold: under",
      "tests": "head e8590370, from ui/ under os-verify-lock: `pnpm exec vitest run packages/app-shell/src/chrome/ConsoleToaster packages/app-shell/src/chrome/consoleToasterAnchor` -> Test Files 5 passed (5), Tests 21 passed (21), VERDICT command-exit 0. Ablation 1 (whole source restored from merge base 436d2840, blob 252cf760; restored to HEAD blob 24ccab6c, git diff HEAD empty, porcelain empty): new file 3 failed / 1 passed (1024/720 case, late-render, non-vacuity red; wide-window control green), drawerClearance-11685 6/6 green. Ablation 2 (scripts/ablation-replace.mjs deletes the contents.observe line, anchor 1 -> 0, restored blob == HEAD): late-render and non-vacuity cases red, 2 failed / 2 passed. ui vitest resolves @object-ui/* to src via ui/vitest.config.mts aliases, so no build between mutation and run. Real layout, one-off Chromium probe of the bundled ConsoleToaster over a hand-built NavigationOverlay-shaped drawer at 1024x768 / 720: merge base toast 644-1000 x 69-123 (the card's reading exactly) covers Edit; head toast 644-1000 x 129-183 clears Edit and more, same with body scrolled 60px; 1440 / 864 toast 195-551 x 24-78 on both.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at e8590370: 34 derived, each run with exit captured before any pipe, 34 x exit 0. --ran: 'Run reconciliation - 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN' (derived zero, every line carries its exit code). Extra local: `pnpm --filter @object-ui/app-shell type-check` after `turbo run build --filter='@object-ui/app-shell^...'` -> exit 0 under the lock (tsconfig.test.json includes src/**/*.test.tsx); eslint over the two touched files exit 0. CI check-runs: in_progress at report time (not waited on, per the standard order).",
      "dogfood_battery": "n/a - no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "3 - mcp__github__issue_read (get x1, get_comments x2); no MCP write tool",
      "api_writes": "2 - fleet-write pr_create (#26426, read back 7705/7705 bytes identical, assignee os-steve) and this os-dev-report comment via post-stamped.mjs; one earlier pr_create payload was refused by the relay's validator with zero writes (empty assignees). git push x3 to the card branch (not REST).",
      "deviations": [
        "PR body line 1-2 are the harness's project attribution block (a comment marker plus a Requested-by line); `Fixes #26418` is line 3. os-dev allows the harness attribution line ahead of Fixes; noted because the standard order says Fixes is the first line.",
        "PR assignee is os-steve although the card has no assignee: the relay refuses an empty assignees list, and the claim's Account is os-steve.",
        "Narrowed: the full app-shell suite (~1725 files) was not run locally; the five ConsoleToaster / toaster-anchor files were. CI Test UI runs the rest."
      ],
      "open_questions": [
        {
          "question": "A first control in the drawer's lower half is ignored (treated as body or footer content) so the toast is never pushed off the window. Keep the half-height cut-off, or tie it to the toast's own height?",
          "options": ["A keep drawer.offsetHeight / 2 (no toast-height constant to maintain)", "B ignore the row only when row bottom + gap + toast height exceeds the drawer height (needs a toast-height reading)"],
          "recommendation": "A, because a header row never sits below mid-drawer and it adds no pixel constant taken from one page"
        }
      ],
      "out_of_scope_findings": [
        "carrier: none - noted, not filed: in a drawer whose first control is a form field (a form sheet), the toast now sits below that first field's row instead of on it; neither is a header row and this card does not change that case (PR Acceptance notes)."
      ]
    }

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uipriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions