Repository navigation
fix(plugin-list): an empty gallery draws the grid empty state, translated (objectui#12217) - #12235
Merged
objectstack-fleet[bot] merged 2 commits intoOct 12, 2026
Merged
Conversation
…slated ObjectGallery drew a hard-coded English "No items to display" in every locale and ignored the view's authored emptyState: ListView drew its empty state for the grid only and handed an empty gallery the empty list. The grid's empty-state block moves into one module-internal component, ListEmptyState. ListView draws it for grid and gallery; ObjectGallery draws it standalone with what it knows (no authored emptyState on the node, no search box, user filter or add-record control; its own filter counts only when it issued the query). The copy is the list.* keys the grid already read, through ListView's own translation hook. Claude-Session: https://claude.ai/code/session_016djJF12Qt14ejKR5Vjt4sK Co-authored-by: Claude <noreply@anthropic.com>
…ate takes the resolved glyph The authored icon name is resolved through `resolveIcon` where it is read, in ListView, and handed to ListEmptyState as a component; the `Inbox` fallback stays in the shared component. ListView.tsx therefore still calls the seam, as the lucide-icon census pins by path. Claude-Session: https://claude.ai/code/session_016djJF12Qt14ejKR5Vjt4sK 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
|
objectstack-fleet
Bot
deleted the
claude/issue-12217-gallery-empty-state
branch
October 12, 2026 11:43
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.
Fixes #12217
Clause-②: no
An empty gallery now draws the same empty state an empty grid draws: the view's authored
emptyState(titleandmessageresolved against the display locale, itsicon), otherwise the localizedlist.*copy. It used to draw a hard-coded English "No items to display" in every locale and ignored the authoredemptyState.What changed
ListEmptyState(new, module-internal:packages/plugin-list/src/ListEmptyState.tsx, not in the package barrel). The grid's inline empty-state block inListViewmoved into it unchanged: the copy selection between the user's query, the view filter and first run (objectui#4155, objectui#11687), theI18nLabelresolution oftitleandmessage(objectui#11227), theInboxglyph fallback, and the add-record action. No second string table, no new key.ListViewdraws it forgalleryas it does forgrid(the empty branch's condition is now grid or gallery). The authored icon name is still resolved throughresolveIconinsideListView, and the component takes the resolved glyph. SoListView.tsxstill calls the icon seam, whichscripts/__tests__/check-lucide-icon-record-names.test.tspins by path, and the change stays inside the claimed file surface.ObjectGallery's empty branch draws it standalone, through the translation hookListViewdraws it with (useListViewTranslation, now a module-level export ofListView.tsx; the barrel does not re-export it) anduseDisplayLocale().H1: the two paths a gallery reaches, and the cloud page
ListViewwithviewType: 'gallery': the console's object list (app-shellObjectViewhandsplugin-view'sObjectViewarenderListViewthat mountsListView) and an interface list page whose visualization is a galleryListView's empty branch was grid-only, so the empty list went throughSchemaRenderertoObjectGallery, which drew the English lineListViewdrawsListEmptyStateitself;ObjectGalleryis not mounted for an empty listobject-gallerynode,plugin-viewObjectView's own non-grid path (norenderListView),RelatedList's mobile card listObjectGallerydrew the English lineObjectGallerydrawsListEmptyStateThe cloud page 「高级 → 我的软件包」 takes the
ListViewpath. It is atype: 'list'page (cloud'ssys-package.page.ts, as objectstack-ai/objectstack#23005's evidence states), rendered by@object-ui/app-shell'sInterfaceListPage, which mountsListViewwithviewTypetaken fromappearance.allowedVisualizations[0]. This is read from source and was not measured on the cloud console: the cloud repository is not attached to this session. That page relays its ownfilter(publisher = 'private') and has no page-levelemptyStateto relay (the spec gap objectstack-ai/objectstack#23005). So it will draw the view-filter wording the grid draws on the same page: in zh 「没有匹配的记录」 / 「没有符合该视图筛选条件的记录。」.RelatedListdraws no card body while its list is empty (its!isEmptyguard), so its mobile gallery reaches the empty branch only in edge windows.H2: what each caller can see
ListViewObjectGallerystandaloneemptyStateschema.emptyState(title, message, icon)ObjectGallerySchemadeclares noemptyState, and adding it is a type change this card rules outhasBaseFilter)filterfilter, only when the gallery issued the query itself (fetchesForItself); rows a host handed in were filtered by the hosthasUserQuery)false)toolbarFlags.showAddRecorduseListViewTranslation,useDisplayLocaleChosen shape:
ListViewdraws the shared component forgalleryas it does forgrid. That is the simplest correct shape, andListViewholds every input.ObjectGallerydraws the component standalone with what it knows. The filter predicate is the sharedhasDeclaredFilter, so the two callers cannot disagree about what a non-empty filter is.H3: i18n
The copy uses the six
list.*keys the grid already read:list.noMatches,list.firstRunTitle,list.noMatchesMessage,list.viewFilterNoMatchesMessage,list.firstRunMessageandlist.addRecord. All six sit underlistin the built-inzhpack, and the provider-less fallback isLIST_DEFAULT_TRANSLATIONS.ObjectGalleryreaches them throughListView's ownuseListViewTranslation. No key is added;pnpm check:i18n-keysis green.H4: other view types' empty branches (measured, not changed)
These are cited by file and quoted text, because AGENTS.md rule #11 bans line addresses in a pull-request body.
packages/plugin-kanban/src/KanbanImpl.tsx: the board-levelDataEmptyStatereadst('kanban.noCards'). It is localized, but it does not read the view's authoredemptyState.packages/plugin-calendar/src/ObjectCalendar.tsx: there is no empty-state copy; an empty calendar draws its grid.packages/plugin-timeline/src/ObjectTimeline.tsx: there is no empty-state copy. The loading placeholder carries a hard-coded English screen-reader line,Loading timeline….packages/plugin-gantt/src/ObjectGantt.tsxandGanttView.tsx: there is no empty-state copy; an empty chart draws its axis.packages/plugin-map/src/ObjectMap.tsx: there is no empty-state copy over a working map. The WebGL-failure fallback list draws hard-coded EnglishNo locations to list., beside the hard-coded failure sentence that startsMap failed to load.packages/plugin-tree/src/ObjectTree.tsx: an empty tree draws hard-coded EnglishNo records(andLoading…while loading), althoughuseTreeTranslationis in scope in that component.packages/plugin-charts/src/ObjectChart.tsx: a localizedDataEmptyStatethroughdashboard.empty.title(inline defaultNo data yet).Tests and gates (all at
d7f71fd, this branch's head)packages/plugin-list/src/__tests__/ObjectGallery.emptyState-12217.test.tsx, 10 rows:ListView: zh first-run copy and the zh add-record label; an authored inline locale map painting its zh-CN entries; an authored plain string drawn as written; and the gallery and the grid drawing the same empty state node for node.filteremptied its own query; and a no-filter control.ListView.tsxandObjectGallery.tsxwere checked out at the basea491b64(hash-equal to the base blobs; on diskNo items to display= 1 andListEmptyState= 0 inListView.tsx). The new file then ran 8 failed, 2 passed: the eight rows that need the fix went red and the two controls stayed green, the direction predicted. Both paths were restored fromHEADunder a trap and are hash-equal to theHEADblobs, withgit diff HEADempty.ListView.emptyVsErrorRole-7132,ListView.emptyStateFilteredCopy(objectui#4155, objectui#11687 rows),ListView.emptyStateI18nLabel-11227andicon-seam-fallbacks-5935.plugin-listfiles (everyObjectGallerytest, everyListViewempty-state test, the gallery-touchingListViewtests):Test Files 40 passed (40),Tests 457 passed (457).ListView.tsx/ObjectGallery.tsxsource or render a gallery through them. These include theInterfaceListPagerelay census, the lucide icon census, theObjectViewgallery tests,RelatedList's mobile gallery and thedefaults-maps-mirror-en-packgate:Test Files 38 passed (38),Tests 845 passed (845).ObjectGallery.fetchGate-7903negative assertion asserted on the old English string, so it would have passed for nothing. It now looks for the shared component's test id instead.turbo run build --filter='@object-ui/plugin-list^...'(12/12).pnpm --filter @object-ui/plugin-list run type-checkechoedtype-checkand ran bothtscprojects, exit 0.tsconfig.test.json's file list includes the new test.dist/index.d.tsnames none ofListEmptyState,useListViewTranslationorhasDeclaredFilter, and neithersrc/index.tsxnorpackage.jsonchanged. The per-file declarations gainListEmptyState.d.ts, andListView.d.tsnow exportsuseListViewTranslation. Theexportsmap exposes only the package root, so neither is reachable through it.check:vi-mock-specifiers,check:test-path-roots,check:unreferenced-sources,check-changeset-presence(5 source files of 1 released package, 1 changeset),check:changeset-claims,check:pending-changeset-literals,check:control-bytes,check:new-line-citations(0 new citations). The derived gates are green too:check:icon-record-names,check:i18n-keys,check:i18n-dead-keys,check:i18n-drift,check:phantom-deps,check:unused-deps,check:vi-mock-inherit,check:vi-mock-override-shapeandcheck:handler-key-reads..ts/.tsxfiles (count read from--format json): 0 errors. The config extendstseslint.configs.recommendedwith noparserOptions.project/projectService, so linting is not type-aware and this diff cannot move a verdict on an untouched file. The fulleslint .run is CI's.check:readme-exports: 386 self-imports unjudged because packages are unbuilt.check:eager-closure: noapps/console/dist/eager-closure.json. The console aliases@object-ui/plugin-listto itssrc, whose barrel already imports bothListViewandObjectGallery, so the newObjectGallerytoListViewedge adds no module to any closure;ListEmptyState.tsxis moved code.check:sdui-registration-pins: no console build. Theobject-galleryregistration is unchanged.Acceptance notes
ObjectGallerySchemadeclares noemptyState, andplugin-viewObjectView's own path (norenderListView) relays a named view'semptyStateto no visualization, its grid included. Only itsrenderListViewbranch relays it. Not changed here: either half is a type or relay change outside this card.No recordsand the map'sNo locations to list.are the same untranslated-empty-copy family as this card. They are listed in H4. Neither is filed here, and neither was measured at a public door.content/docsorpackages/plugin-list/README.mddescribes the list's empty-state copy, so nothing went stale.Generated by Claude Code