Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

A read-only view's … menu in Manage views opens EMPTY (a 180×10px strip), and its tab menu opens on a leading separator #10209

Closed

Description

@baozhoutao

Symptom

On an object whose every view is code-defined (src/views/*.view.ts — no sys_view overlay row), so ObjectView stamps readonly: true on the tab:

  1. Manage views → the row's … button opens a menu with nothing in it. Measured in a real browser: role="menu" bounding box {width: 180, height: 10}, innerHTML === ''. Rendered, that is a thin strip of bg-popover straddling the row's bottom border — it reads to a user as a menu that is clipped or occluded and unclickable, not as an empty one. Nothing is occluding it; there is nothing in it.
  2. The tab's ▾ menu opens on a leading role="separator" above its single Manage all views… entry.

Cause

Both menus ask a different question in the guard than in the entries beneath it.

  • ManageViewsDialog's … trigger renders on (onDelete || onDuplicate || onConfigView || onSetDefault) — whether a callback is wired. Every entry under it is also gated on !isReadonly. The one entry isReadonly does not suppress is Duplicate, and the console deliberately wires no onDuplicate (ADR-0034 (v2/未来): 仅视图的 per-user 个性化(user 覆盖 scope) #1520). So on a read-only row the guard passes and every entry drops out.
  • ViewTabBar's separators state their own condition (onDeleteView && !isReadonly opens a fragment that also holds the separator; the manage entry opened one unconditionally) rather than asking "did anything render above me". Its trigger guard has the same shape as the dialog's, so a read-only tab with no onManageViews wired opens a fully empty dropdown too.

Reproduction

ViewTabBar / ManageViewsDialog with a single { readonly: true, isDefault: true } view and the console's prop set (no onDuplicate).

Not in scope here

Whether a code-defined view should be read-only at all is a separate product question (ObjectView classifies isSystem = !saved), as is ViewTabBar's allReadonly rule, which suppresses the per-tab lock badge when every view is read-only — on an object with exactly one, code-defined view that removes the only signal explaining why the menu is nearly empty.

Activity

  1. baozhoutao commented on Sep 22, 2026

    @baozhoutao
    ContributorAuthor

    Root cause found and filed separately as objectui#10210.

    This issue describes the symptoms on the menus (the empty 180×10px popover, the leading
    separator). What actually puts a view into the readonly state that triggers them is a shape
    mismatch between the view-config save and listViews()'s classifier — measured end to end on
    an empty database in objectui#10210.

    Correcting one claim in the description above: it says the state arises on "an object whose
    every view is code-defined". That is not sufficient on its own. Measured on an empty DB, a
    code-defined view opens with its full six-entry menu; it collapses only after
    Edit view config → Save, which writes a flat body that the classifier then reads back as a
    personalization overlay. The classification statement in the last section
    ("ObjectView classifies isSystem = !saved") is accurate; what was wrong was the implied
    population.

    These two remain independent: fixing objectui#10210 removes the most common route into the
    state, but other read-only paths reach the same menus, so the empty popover is still
    reachable. The branch claude/quirky-wozniak-h2bt8o carries the cosmetic fix and is not
    proposed for merge — it is pushed for reference only, pending the maintainer's call on
    objectui#10210's direction.


    Generated by Claude Code

  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    分诊首次定级:priority:p3 · bug · domain:ui · pm:queue —— 只读视图的"…"菜单打开是空的、标签菜单以分隔线开头;与根因 #10210 相互独立

    Path: packages/plugin-view/src/ManageViewsDialog.tsx(行尾"…"触发器的判断)+ ViewTabBar(分隔线与触发器的判断)

    Triage: lands in @object-ui/plugin-view ⇒ domain:ui, bug, priority:p3, pm:queue; rationale: the "…" trigger renders when any callback is wired (onDelete || onDuplicate || onConfigView || onSetDefault) while every entry beneath it is also gated on !isReadonly, so a read-only row opens an empty 180×10 strip; cosmetic and misleading but nothing is lost, and — as the filer's own follow-up says — independent of #10210, because other read-only paths (insufficient permission) reach the same menus.

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T16:19Z。⛔ 不认领、不派发。本席读完了卡面和全部 1 条评论(提卡人的更正 5772743049),并在 objectui main(06b82b8)上核对。

    本席实测

    定级说明

    执行要点

    1. 可以从参考分支起步,但以 main 重新核对。
    2. 触发器的条件改成"下面至少有一项会渲染";分隔线改成"上面确实渲染了东西才画"。
    3. 反向对照:可编辑视图的菜单仍是完整的六项。
    4. 卡面末节的两个产品问题(代码定义的视图该不该只读、allReadonly 把唯一的锁标记也藏了)⛔ 不在本卡范围。

    Generated by Claude Code

  3. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 24, 2026
  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 3 — domain:ui execution seat 3
    Session: session_01D486C1axmXnrkJMNUfz2eb
    Branch: claude/issue-10209-readonly-view-menus-not-empty
    Worktree: objectui-issue-10209
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: packages/plugin-view/src/ManageViewsDialog.tsx (the row … trigger guard), packages/plugin-view/src/ViewTabBar.tsx (separators and the trigger guard), their tests under packages/plugin-view/src, one .changeset/10209-…md (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (default judgement tier) — dispatch-gates.mjs --repo objectstack-ai/objectui --tier … answers 「Derive 'objectstack-ai/objectui' from a checkout OF 'objectstack-ai/objectui' — this script exists only in 'objectstack-ai/objectstack'」 ⇒ no path-derived mandate; tier is this seat's judgement.
    Clause-②: no
    Thread-read: 5817909733
    Serial constraints cleared: open PRs and live pm:dispatched claims read 2026-09-24T16:33Z — none touches either file. This seat's own PR objectui#10283 holds packages/plugin-view/src/ObjectView.tsx, which is a different file ⇒ disjoint. objectui#10210 (the root cause, in ObjectView / data-objectstack) is file-disjoint, per triage 5817909733.

    Why Clause-②: no: menus render only entries that exist; no accepted prop value or export changes.


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 10209,
      "status": "done",
      "branch": "claude/issue-10209-readonly-view-menus-not-empty",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10306",
      "session": "session_01D486C1axmXnrkJMNUfz2eb",
      "premise_still_valid": true,
      "summary": "Premise re-derived on main 8b1f066: ManageViewsDialog's row trigger was gated on onDelete||onDuplicate||onConfigView||onSetDefault while every entry was also gated on !isReadonly (console wires no onDuplicate). ViewTabBar's dropdown separators stated their own conditions, and its trigger used the same callback-shaped guard. The same file's tab CONTEXT MENU had the identical defect: a leading separator on read-only tabs, and an empty menu on any tab with no surviving entry, which includes every tab for a non-admin console user. Each of the three menus now resolves every entry once to the handler it will run (or undefined). Entries, separators and the trigger read those values: the trigger/ContextMenu wrap renders only if some entry renders, and a separator only sits between two rendered groups. One deliberate adjustment: ManageViewsDialog no longer offers Rename (menu or double-click) without onRename, because it committed to onRename?.() as a no-op, and the new trigger predicate would otherwise have exposed it on no-callback hosts. The console wires onRename, so console behaviour is unchanged. Editable views keep every entry (reverse control pinned). Assignee was already set by PM (os-project-manager), untouched.",
      "tests": "All at head ebc4146. (1) pnpm exec vitest run packages/plugin-view/ --maxWorkers=2 (repo root, via os-verify-lock): 'Test Files 45 passed (45)', 'Tests 414 passed (414)', VERDICT command-exit 0. (2) pnpm --filter @object-ui/plugin-view type-check after pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-view^...' build (16 projects, VERDICT exit 0): VERDICT command-exit 0. tsc -p tsconfig.test.json --listFiles includes both touched test files (grep count 2). (3) New pin file packages/plugin-view/src/__tests__/readonlyViewMenus-10209.test.tsx has 10 tests asserting the ordered child roles of the open menu plus trigger presence. (4) Reverse verification after the fix was committed: git checkout 8b1f066 of both source files, with markers on disk hasMenuEntry=0 and dropdownHasEntry=0, then the new file: 'Tests 6 failed | 4 passed (10)' (exit 1). The 6 red are the empty or leading-separator cases; the 4 green are the 3 editable reverse controls and read-only+Duplicate, which were already correct. Restored via git checkout HEAD -- (trap-guarded, absolute paths): markers 2 and 6 back, git diff HEAD 0 bytes, both hash-object values equal HEAD blobs (HASH-MATCH). No dist in path: the vitest alias maps @object-ui/plugin-view to src, and the test imports relative. (5) The existing ViewTabBar.setDefaultVisibility.test.tsx went red 2/4 on the fix, as expected (read-only tab without onManageViews now has no trigger). The test now wires onManageViews and still asserts that set-default, rename and delete are absent. (6) Gates, each exit captured to a file: check-changeset-presence 0, check-changeset-no-major 0, check:changeset-claims 0, check:pending-changeset-literals 0, check:new-line-citations 0 ('0 new citation(s)'), check:control-bytes 0, check:test-path-roots 0, check:vi-mock-specifiers 0, check:unreferenced-sources 0. (7) Lint narrowed: eslint --no-inline-config --format json on the 4 touched tsx files gives files 4, errors 0, warnings 5, all on untouched pre-existing lines (no-explicit-any in the label helpers, set-state-in-effect in existing effects). eslint.config.js has no parserOptions/projectService (grep count 0), so linting is not type-aware and the diff cannot move untouched files. The population for 'eslint .' covers packages/plugin-view (the files were linted, not ignored). Full pnpm lint: NOT MEASURED locally, CI-owned. CI: in_progress, not awaited. Spec Main Shape Gate red is the known upstream drift objectui#10287.",
      "mcp_calls": "0",
      "api_writes": "2 — (1) pr_create through the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches (request fw-20260924T164612Z-6d4210, run 36029664482 success), which executed POST /repos/objectstack-ai/objectui/pulls (draft) and created #10306 as objectstack-fleet[bot]. The body was read back byte-identical. (2) This os-dev-report comment via post-stamped on objectui#10209. git push x2 (empty branch probe, then commit ebc4146) is not REST. Zero label writes, zero PR-body PATCH.",
      "open_questions": [],
      "out_of_scope_findings": [
        "No new class a/b/c finding filed. Noted only (Acceptance notes): the two product questions triage fenced (should code-defined views be read-only; allReadonly hides the only lock badge) remain as on the card; objectui#10210 untouched.",
        "Deviation (scope, declared): the ViewTabBar tab context menu fix goes beyond the card's literal dropdown wording. It is the same defect class in the same file, mechanical, with no other claim on the file and no new gate family, so the bounded in-place exemption applies. It is named in the PR body with pinned evidence.",
        "Opportunistic AGENTS.md #11 repair: two cross-file ViewTabBar.tsx line addresses in ViewTabBar.setDefaultVisibility.test.tsx's header were replaced by test ids.",
        "Files changed: .changeset/10209-readonly-view-menus-not-empty.md (+19), packages/plugin-view/src/ManageViewsDialog.tsx (+42/-17), packages/plugin-view/src/ViewTabBar.tsx (+80/-43), packages/plugin-view/src/__tests__/ViewTabBar.setDefaultVisibility.test.tsx (+5/-1), packages/plugin-view/src/__tests__/readonlyViewMenus-10209.test.tsx (+186, new). Head ebc4146, base 8b1f066. Worktree removed after PR open. Reference branch claude/quirky-wozniak-h2bt8o was read, not cherry-picked (its lock tooltip was left out as out of scope)."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT — PR objectui#10306 at ebc4146 · pm:dispatched → pm:blocked on objectui#10287

    domain:ui seat 3, session_01D486C1axmXnrkJMNUfz2eb, 2026-09-24T17:04Z. Reviewed against the diff, the tree and CI.

    Implemented-by:  claude/issue-10209-readonly-view-menus-not-empty
    Reviewed-by:     session_01D486C1axmXnrkJMNUfz2eb
    
    item reading
    shape draft · base main · line 1 Fixes #10209 · line 2 Clause-②: no
    scope ManageViewsDialog.tsx, ViewTabBar.tsx, one new pin file, one edited pin, one patch changeset, all inside the claimed surface. The tab context-menu fix and the Rename gate are declared deviations, and both were accepted by the review
    contract review PASS at ebc4146 (61/61 at tier), recorded on the PR. The changeset prose was reviewed as a review surface
    CI green at head except Spec Main Shape Gate, the upstream doc-nav drift anchored at objectui#10287

    The gate runs on merge_group, so this PR waits outside the queue. The card body gains Blocked-by: #10287 and Unlock-action: re-check PR #10306. When objectui#10287 lands: merge main, read every check at the new head, then ready and auto-merge.


    Generated by Claude Code

  7. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    🟢 LANDED — PR objectui#10306 merged as b07de29d1 (merge queue)

    domain:ui seat 3, session_01D486C1axmXnrkJMNUfz2eb, 2026-09-24T19:11Z. Merged 2026-09-24T19:09Z.

    Verified by content on objectui origin/main: hasMenuEntry gates the ManageViewsDialog.tsx row menu (lines 193, 348); dropdownHasEntry / dropdownHasEntryAboveDelete / dropdownHasEntryAboveManage gate the ViewTabBar.tsx dropdown and its separators (lines 432–656); the pin packages/plugin-view/src/__tests__/readonlyViewMenus-10209.test.tsx is present. The squash commit carries no model-named trailer.

    Fixes #10209 closed the card completed, but the auto-close does not strip labels; pm:dispatched is removed in this stroke. priority:p3, bug and domain:ui stay.


    Generated by Claude Code

  8. added 2 commits that reference this issue on Sep 28, 2026
    b07de29
    baf98cd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions