Repository navigation
feat(app-shell): an object leaf's Studio panel edits its list view (objectui#11823, step 1) - #11868
Merged
objectstack-fleet[bot] merged 6 commits intoOct 8, 2026
Merged
Conversation
The Properties panel of an `object` leaf in Studio's Interfaces pillar edits the list view its canvas shows: columns, filter and sort, saved to the package draft as a `view` item through the metadata draft door, with its own version guard and the pillar's autosave. The canvas renders the panel's buffer, so an edit shows there before it is published. A read-only package shows the panel disabled and writes nothing. Other studio-canvas types keep the no-blocks statement. Step 1 of objectui#11823. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
…lumns, and edits list views only A default list view that does not exist yet shows on the canvas with the columns the running app shows such an object, which the first edit starts from. An item at the view's name that is not a list view (a form view, or a body with no columns) is shown as such and never edited or saved over. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
…st-view-inspector Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
…r reads one A refused list-view save shows the Studio's author sentence with the raw refusal behind a Details disclosure, read in the designer locale at render (the refusal readers of metadataError.ts), and a load failure shows as its text. A read-only package shows only the read-only note. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…iew cache key The panel buffer's identity was spelled `view:` + the view name, the adapter's view cache key that only invalidateViewKeys may spell (objectui#4373), so the structural guard in viewCacheInvalidation.guard.test.tsx refused the file. One helper, listViewBufferKey, now spells it `listView:` + the name at all three sites. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
…st-view-inspector Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 8, 2026
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-11823-list-view-inspector
branch
October 8, 2026 03:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #11823 (item 1)
Clause-②: no
What this does
An
objectleaf's Properties panel in Studio's Interfaces pillar now edits the list view its canvas shows: Columns (add, remove, drag, or select a row and move it up or down), Filter and Sort. Edits autosave to the package draft and show on the canvas at once: ADR-0084's "source + preview", with preview = runtime. Steps 2 (dashboard and report create flow) and 3 (page create flow) of objectui#11823 are not in this PR, and the card stays open for them.navEntryListTarget). An entry that lands on a named view edits that view (matched withresolveViewId; a name that matches nothing isOBJECT.NAME, created by the first edit). Any other entry edits the object's default list view: the merged view flaggedisDefault, elseOBJECT.default, the nameexpandViewContainergives a container's defaultlist.viewitem in its record shape,{ name, object, viewKind: 'list', isDefault?, config: { type, data, columns, filter?, sort? } }, sent byclient.save('view', name, body, { mode: 'draft', packageId }): the pillar's ownMetadataClient, behind its ownuseDraftSaveGuard(objectui#11773), debounced by the pillar'suseDraftAutoSave. Both are called as they are. No new endpoint and no new client.defaultListColumnsFromObject, imported fromviews/ObjectView.tsx). The canvas shows those columns too. The first edit creates the view from them.readOnly, and nothing is written.columns) is shown as such, and is never edited or saved over.issueRefusalfrommetadataError.ts(imported, not edited) gives the author sentence, with the raw refusal under Details.studioCanvasNoBlocks.Measurements (H1 to H4)
Taken against a published
@objectstack/*17.7.0 server (objectstack dev --fresh, the release this repo resolves), with a writable Studio package (POST /api/v1/packages) and a code package, and this branch's console under Vite, driven by Playwright and Chromium.59ce048the panel of an object leaf showed onlystudioCanvasNoBlocks(browser reading). For a plain entry the canvas rendered{ type: 'object-view', objectName }with nolistViews. That is a grid of the object's fields (Title, Amount, Stage, Due, Owner), not the package's default list view. On the same data, the running app (/apps/pst_app/pst_order) rendered the published default view: Title and Amount, filtered and sorted. For an object with no view, the canvas also showed Owner, which the runtime's fallback list leaves out. For aviewNameentry, the canvas rendered the cache's named view. The panel now edits the entry's named view, or else the default view. The canvas shows the panel's view in both cases.viewrecords (GET /meta/view). A package'sdefineViewcontainer arrives expanded:OBJ.defaultwithisDefault: true, andOBJ.KEYfor each named view.GET /meta/object/NAMEcarries nolistViews; the console mergesviewitems intoobjectDef.listViews(mergeViewsIntoObjects). The draft door accepted both a record and a container body (PUT /meta/view/NAME?mode=draft&package=…, 200). But?preview=draftlisted the record and not the container, because containers are never listed.publish-draftspublished the record, andGET /meta/viewthen served it. The door refused a sort entry spelleddirection(422). It accepted a draft for a code package's view as a customization overlay, so the read-only gate is Studio's ownreadOnly, as for every other inspector. The object's embeddedlistViews(ObjectSchema.listViews, meant for system objects) is not written: it belongs to the object definition and the Data pillar.useMetadata, published rows), and only for aviewNameentry, so a draft never showed there.StudioObjectRecordsCanvas's body now also reads a Studio-internal context,StudioCanvasListViewContext. It sits beside the published props, likeStudioCanvasNavEntryContext, and holds the panel's buffer. The function's signature andStudioCanvasPreviewPropsare unchanged, and nothing is added toindex.ts.plugin-list'sListView(ViewSettingsPopover, not exported), so it cannot be imported. Three editors are reused by import, unedited:FieldsListEditor.FilterBuilder, through the designer's exportedfilter-builderwidget, which writes the spec'sViewFilterRule[].SortBuilderfrom@object-ui/components, inside a popover. Placed inline in the 18rem rail, its fixed label and direction widths leave the field select about 36px. It has no disabled prop, so the read-only state disables its trigger instead. Its rowidis stripped on write, because the spec refusesidon a sort entry.The saved shape (browser reading, writable package)
After removing one seeded column of an object that had no view:
Every saved body in the pins parses with the spec's
ViewItemSchema(installed@objectstack/spec17.7.0).Real-browser reading (dev-only harness, not committed)
Console from this branch, backend
@objectstack/*17.7.0, Chromium (/opt/pw-browsers/chromium):All Orderswith columnstitle,amount, filter Stage, and sort "Amount descending". The canvas showed Title and Amount, rows Charlie 20 and Alpha 10, which is the runtime's own rendering.PUT …/view/pst_order.default?mode=draft&package=com.probe.studiofollowed. The header's Changes count went to 1."order":"asc".wonchanged the canvas to Bravo 30 and Delta 5, and the PUT carried{"field":"stage","operator":"equals","value":"won"}.name,qty,color. The canvas showed Name, Quantity and Color, as the running app does (before this change, the canvas also showed Owner). Removingcolorcreated the view (the PUT above), and the canvas dropped the column. After the package's drafts were published, the running app listed Name and Quantity.writable: false): the panel was read-only, with no drag handles, no remove buttons, no Add field button, and disabled Filter and Sort triggers. No PUT was sent.File surface
Every file is on the claim's surface:
studio-design/ObjectListViewInspector.tsx(new): the panel, the buffer hook (load, the version guard, save) and the pure helpers.studio-design/StudioDesignSurface.tsx:InterfacesPillar, the list view's draft write path, which the claim asked to be named before editing: oneuseObjectListViewDraftcall and oneuseDraftAutoSavecall, placed right after the leaf autosave;None of seat 3's objectui#11786 regions is edited: the
useDraftAutoSavedefinition, the Data pillar'sdoSave, the Automations save anddoPublish.studio-design/studio-canvas-preview.tsx: the new context,listViewsOfexported at module level (not fromindex.ts), and the body ofStudioObjectRecordsCanvas.metadata-admin/i18n.ts: newengine.studio.inspector.listView.*rows, en and zh.Tests beside these, and
.changeset/11823-list-view-inspector.md(@object-ui/app-shellpatch).Two existing pins changed meaning, because the object leaf's rail is no longer the no-blocks statement:
StudioDesignSurface.studioCanvasLeaf.test.tsxnow takes the no-blocks pins on a stubreportstudio canvas, and adds an object-leaf pin for the new panel.StudioDesignSurface.selectionLeafScope.test.tsxnow waits for the panel instead of the statement. Its assertions are unchanged.Tests and gates (local
git rev-parse --short HEAD=f5610fa, which includesmainat5aa7f55)pnpm --workspace-concurrency=2 --filter "@object-ui/app-shell^..." build(via os-verify-lock)VERDICT command-exit 0, 29 of 47 projectspnpm --filter @object-ui/app-shell type-check(tsc --noEmit && tsc -p tsconfig.test.json)tsc -p tsconfig.test.json --listFileslists all 5 touched test filespnpm exec vitest runonStudioDesignSurface*,studio-canvas-preview*andObjectListViewInspector*Test Files 54 passed (54),Tests 341 passed (341)pnpm exec vitest runon the other 44studio-designtest files, plus the 5 tests that read the designer i18n tablesTest Files 49 passed (49),Tests 381 passed (381)pnpm check:control-bytescheck-control-bytes: OKpnpm check:new-line-citationsVERDICT new-cross-file-line-citations: 0 new citation(s)pnpm check:changeset-claimsNo pending changeset names a file this change touches.pnpm check:pending-changeset-literalsNo test source names a pending changeset.pnpm check:i18n-designer-parityEvery en row has a zh row, and every shared row carries the same placeholders.pnpm check:spec-symbolspnpm check:i18n-keys/check:i18n-driftNo designer-table en value changed in this range.pnpm check:vi-mock-specifiers/-inherit/-override-shapepnpm check:test-path-roots/check:metadata-write-doors/check:unreferenced-sourcesnode scripts/check-changeset-presence.mjs/check-changeset-no-major.mjs9 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)/ nomajorLint (narrowed, measured).
eslint --format jsonran over the 9 touched TS files, as the package script runs it (eslint .frompackages/app-shell): 9 files, 0 errors, 26 warnings, all of themreact-refresh/only-export-componentsorreact-hooks/*warnings, of the kinds the sibling studio-design files already carry. The rooteslint.config.jssets no type-aware parser options (project/projectService), so this diff cannot move a verdict on a file it does not touch. The repo-widepnpm lintand the full test farm are CI's.Reverse check. With the fix committed,
StudioDesignSurface.tsxwas set back tomain's blob (git checkout 5aa7f55 -- …, under a trap restoring fromHEAD). The new pin file then went red in the predicted direction:Tests 6 failed | 1 passed (7). Every panel pin failed withUnable to find an element by: [data-testid="studio-list-view-inspector"], and the rail rendered the no-blocks statement. The CONTROL pin stayed green. After restoring, the file's blob equalsHEAD's (9cb25ec…) andgit diff HEADis empty.Acceptance notes
viewNameentry naming a view that does not exist yet: the panel shows the seed columns and the not-created note, while the canvas keeps its existing fallback (the plain grid) until the first edit. The runtime's landing for an unresolved/view/NAMEwas not measured.FieldsListEditoris reused as it is. A row shows a column's own label, else its field name. The add-field popover still offers a field that is already a column (badged "Added") and system fields. Adding one twice writes a duplicate column, which the draft door accepts. Carrier: none._packageId, a spec-declared protection field, and saves send it back, as the pillar's leaf drafts do.Session:
https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7zGenerated by Claude Code