Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .changeset/7192-retire-seven-unmounted-detail-exports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
'@object-ui/plugin-detail': minor
'@object-ui/i18n': minor
---

**BREAKING:** `@object-ui/plugin-detail` no longer exports seven components that
nothing registered and nothing mounted (objectui#7192, objectui#7175). This
narrows the package's published surface. It is declared `minor`, not `major`,
because this fixed release group follows the `@objectstack` major (AGENTS.md
§9); the breaking change is stated here instead.

Removed, with every type exported beside them:

- `CommentInput`, with `CommentInputProps`
- `DiffView`, with `DiffViewProps`, `DiffFieldType`, `DiffMode` and `DiffLine`
- `InlineCreateRelated`, with `InlineCreateRelatedProps`,
`RelatedFieldDefinition` and `RelatedRecordOption`
- `MentionAutocomplete` and the helper `createMentionFromSuggestion`, with
`MentionAutocompleteProps` and `MentionSuggestionItem`
- `PointInTimeRestore`, with `PointInTimeRestoreProps` and `RevisionEntry`
- `RecordNavigationEnhanced`, with `RecordNavigationEnhancedProps`
- `RelationshipGraph`, with `RelationshipGraphProps` and `GraphNode`

Why they went: none of the seven was passed to `ComponentRegistry.register`, so
metadata had no type string to name any of them, and none was mounted anywhere
in this repository. The downstream readings agree: `cloud` references none of
the seven names (objectstack#14187), and `hotcrm` depends on no `@object-ui/*`
package at all. The maintainer ruled to delete them; the ruling is relayed on
objectui#7192.

Not affected: `RichTextCommentInput` and its `MentionSuggestion` type (the
composer `RecordActivityTimeline` mounts), `RecordComments` (`DetailView`
mounts it) and `extractMentions` all stay exported.

`@object-ui/i18n`: the `detail.*` keys that only these components read are
removed from all ten locale packs, and the matching rows from
`DETAIL_DEFAULT_TRANSLATIONS`:

- `PointInTimeRestore`'s ten, added by objectui#7163: `revisionHistory`,
`noRevisions`, `revisionFieldsChanged`, `revisionFieldsChangedOne`,
`revisionPreview`, `revisionSnapshot`, `restoreConfirm`, `restoring`,
`confirmRestore`, `restoreToPoint`
- `emptyValue`, an older key whose last reader was `PointInTimeRestore`
- `DiffView`'s five: `unifiedDiff`, `sideBySideDiff`, `noChanges`,
`previousVersion`, `currentVersion`
- `RecordNavigationEnhanced`'s six: `firstRecord`, `previousRecordKey`,
`nextRecordKey`, `lastRecord`, `searchWhileNavigating`, `searchRecords`

Keys these components shared with surfaces that stay (`detail.recordOf`,
`detail.noRecords`, `detail.cancel`, `detail.activityEmptyValue`, the
relative-time keys) are kept. A host that read one of the removed keys itself
now gets the raw key back.

Other entries in this same release describe work on `PointInTimeRestore` and
`DiffView` (their translation and display-locale fixes). That work shipped in
components this entry removes.

**Migration:** there is no replacement. If you import one of these components,
copy its source file (`packages/plugin-detail/src/`, the file named after the
component) from a release tag that still ships it, for example
`@object-ui/plugin-detail@17.5.0`, into your own code. If you need one of them
back in the package, open an issue that says who uses it and where.
12 changes: 0 additions & 12 deletions content/docs/plugins/plugin-detail.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ The Feed & Chatter system provides Airtable-style record-level collaboration. It
|-----------|---------|
| `RecordChatterPanel` | Main wrapper panel (bottom/right/left positioning, collapsible) |
| `RecordActivityTimeline` | Core activity feed renderer (filter, pagination, actor display) |
| `CommentInput` | Comment submission with Ctrl+Enter shortcut |
| `FieldChangeItem` | Field change history display (old → new values) |
| `MentionAutocomplete` | @mention suggestion dropdown |
| `ReactionPicker` | Emoji reaction selector |
| `ThreadedReplies` | Collapsible reply threading |
| `SubscriptionToggle` | Bell notification toggle |
Expand Down Expand Up @@ -444,15 +442,6 @@ decision as a defect is how a warning channel gets ignored:
| `onToggleSubscription` | `(subscribed: boolean) => void` | — | Toggle subscription callback |
| `className` | `string` | `''` | Additional CSS classes |

### CommentInput

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `onSubmit` | `(text: string) => void` | required | Comment submit callback |
| `placeholder` | `string` | `'Add a comment...'` | Input placeholder text |
| `disabled` | `boolean` | `false` | Disable input |
| `className` | `string` | `''` | Additional CSS classes |

### ReactionPicker

| Property | Type | Default | Description |
Expand Down Expand Up @@ -517,7 +506,6 @@ import type {
import type {
RecordChatterPanelProps,
RecordActivityTimelineProps,
CommentInputProps,
FieldChangeItemProps,
ReactionPickerProps,
ThreadedRepliesProps,
Expand Down
27 changes: 27 additions & 0 deletions docs/audits/2026-09-plugin-detail-downstream-consumer-census.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,33 @@ makes that decision *possible*, not the decision.

---

## Conclusion (2026-09-24): the maintainer ruled **delete**

The decision this audit made possible has since been taken. On 2026-09-24 triage put the
question "delete the seven unregistered, unmounted `plugin-detail` exports?" to the
maintainer with a recommendation to delete, and relayed the reply on objectui#7192 (triage
comment 5819441716, which quotes it verbatim). The reply declined two other items by number
and approved the rest. The seven exports were among the rest, so the ruling is **delete**.

- **The gap this audit left open was closed first.** `cloud` was read on
objectstack#14187 (all seven names, controls firing, zero). `hotcrm` is zero by
construction: it depends on no `@object-ui/*` package (the objectui#7175 reading of
2026-09-04).
- **Delivered by the pull request that closes objectui#7192 and objectui#7175**, on branch
`claude/issue-7192-retire-seven-unmounted-detail-exports`. It deletes all seven components
(`CommentInput`, `DiffView`, `InlineCreateRelated`, `MentionAutocomplete`,
`PointInTimeRestore`, `RecordNavigationEnhanced`, `RelationshipGraph`), their own tests,
their value and type exports from the barrel, and the i18n keys that only they read.
Its changeset lists every removed export and the migration.
- **No replacement is kept "in case".** If a real consumer turns up, it gets a new card that
says who needs the component and where, and the component comes back on that evidence.

The rest of this document is the audit as written on 2026-09-01. Its "does not retire" and
"does NOT claim" statements describe what this measurement established on its own, and they
stay as that record.

---

## Summary

| Population | Channel | Positive control | `PointInTimeRestore` | Verdict |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ const COMMENT_MARKER = 'A comment on the record';
/** The panel's own chrome — present whenever it mounts, empty feed or not. */
const PANEL_HEADING = 'Discussion';
const PANEL_EMPTY = 'No comments yet';
/** `CommentInput`'s textarea — the write surface the ruling is about. Read by
/** The composer's textarea (`RichTextCommentInput`, as `RecordActivityTimeline`
* mounts it) — the write surface the ruling is about. Read by
* PLACEHOLDER, not by the submit button: that button mounts only once the box
* has text, so counting it reads zero on a panel that is fully open for
* writes. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*
* A large amount of existing coverage addresses these controls by their
* English name with NO `I18nProvider` mounted (`discardGuard.test.tsx`,
* `InlineCreateRelated.closeButtonName.test.tsx`, e2e specs). The safe
* e2e specs). The safe
* translation's English fallback is what keeps those green, so it is pinned
* here explicitly rather than assumed.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
* `DialogContent` consumer in the repo, and a large amount of existing
* coverage addresses those controls by their ENGLISH accessible name with no
* provider in the tree — `packages/plugin-form/src/discardGuard.test.tsx`
* (`getByRole('button', { name: 'Close' })`) and
* `packages/plugin-detail/src/__tests__/InlineCreateRelated.closeButtonName.test.tsx`
* (`/^Close$/`) among them. A `t()` call without a working default renders the
* (`getByRole('button', { name: 'Close' })`) among them. A `t()` call without a working default renders the
* raw `common.close` key and breaks all of it — in other packages' suites, not
* this one's.
*
Expand Down
3 changes: 1 addition & 2 deletions packages/components/src/lib/close-label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
* The no-provider path must stay English. A large number of existing unit
* tests and e2e specs address dialogs and drawers by their English accessible
* name with no `I18nProvider` mounted (e.g.
* `packages/plugin-form/src/discardGuard.test.tsx`,
* `packages/plugin-detail/src/__tests__/InlineCreateRelated.closeButtonName.test.tsx`),
* `packages/plugin-form/src/discardGuard.test.tsx`),
* and a primitive that rendered a raw `common.close` key there would break all
* of them. `createSafeTranslation` probes its test key and falls back to the
* defaults map below when translations are not configured, so "no provider"
Expand Down
27 changes: 0 additions & 27 deletions packages/i18n/src/locales/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,6 @@ const ar = {
viewAll: "عرض الكل",
new: "جديد",
add: "إضافة",
emptyValue: "—",
comments: "التعليقات",
searchComments: "البحث في التعليقات…",
addCommentPlaceholder: "أضف تعليقاً… (Ctrl+Enter للإرسال)",
Expand Down Expand Up @@ -999,11 +998,6 @@ const ar = {
attachmentsLoadFailed: "تعذر تحميل مرفقات هذا السجل.",
attachmentsApiUnavailable: "قائمة المرفقات غير متاحة على هذا الكائن.",
retryLoadAttachments: "إعادة المحاولة",
unifiedDiff: "عرض موحد",
sideBySideDiff: "عرض جنباً إلى جنب",
noChanges: "لا توجد تغييرات",
previousVersion: "السابق",
currentVersion: "الحالي",
discussion: "المناقشة",
showDiscussion: "إظهار المناقشة ({{count}})",
hideDiscussion: "إخفاء المناقشة",
Expand All @@ -1018,13 +1012,7 @@ const ar = {
writeComment: "اكتب تعليقاً…",
subscribedTooltip: "مشترك — انقر لإلغاء الاشتراك",
unsubscribedTooltip: "الاشتراك في الإشعارات",
firstRecord: "السجل الأول (Home)",
previousRecordKey: "السجل السابق (←)",
nextRecordKey: "السجل التالي (→)",
lastRecord: "السجل الأخير (End)",
noRecords: "لا توجد سجلات",
searchWhileNavigating: "البحث أثناء التصفح",
searchRecords: "البحث في السجلات…",
allActivity: "كل النشاط",
commentsOnly: "التعليقات فقط",
fieldChangesFilter: "تغييرات الحقول",
Expand Down Expand Up @@ -1139,21 +1127,6 @@ const ar = {
fileCount: "{{count}} ملف (ملفات)",
fileCount_one: "{{count}} ملف",
fileCount_other: "{{count}} ملفات",
// objectui#7163 — PointInTimeRestore's revision-history chrome. The file
// used no translation hook at all, so every one of these read English in
// every session; swept in one pass rather than converting the timestamps
// alone. `Cancel`, `(empty)` and the empty-value dash reuse the keys this
// namespace already has, so only these ten are new.
revisionHistory: 'سجل المراجعات',
noRevisions: 'لا توجد مراجعات مسجلة',
revisionFieldsChanged: 'تم تغيير {{count}} حقول',
revisionFieldsChangedOne: 'تم تغيير {{count}} حقل',
revisionPreview: 'معاينة المراجعة',
revisionSnapshot: 'حالة السجل في هذه النقطة',
restoreConfirm: 'سيؤدي هذا إلى استعادة السجل إلى حالته في {{when}}. هل تريد المتابعة؟',
restoring: 'جارٍ الاستعادة…',
confirmRestore: 'تأكيد الاستعادة',
restoreToPoint: 'الاستعادة إلى هذه النقطة',
},
chart: {
loading: "جارٍ تحميل الرسم البياني…",
Expand Down
27 changes: 0 additions & 27 deletions packages/i18n/src/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,6 @@ const de = {
viewAll: "Alle anzeigen",
new: "Neu",
add: "Hinzufügen",
emptyValue: "—",
comments: "Kommentare",
searchComments: "Kommentare suchen…",
addCommentPlaceholder: "Kommentar hinzufügen… (Strg+Enter zum Senden)",
Expand Down Expand Up @@ -989,11 +988,6 @@ const de = {
attachmentsLoadFailed: "Die Anhänge dieses Datensatzes konnten nicht geladen werden.",
attachmentsApiUnavailable: "Die Anhangsliste ist für dieses Objekt nicht verfügbar.",
retryLoadAttachments: "Erneut versuchen",
unifiedDiff: "Einheitliche Ansicht",
sideBySideDiff: "Nebeneinander-Ansicht",
noChanges: "Keine Änderungen",
previousVersion: "Vorherige",
currentVersion: "Aktuelle",
discussion: "Diskussion",
showDiscussion: "Diskussion anzeigen ({{count}})",
hideDiscussion: "Diskussion ausblenden",
Expand All @@ -1008,13 +1002,7 @@ const de = {
writeComment: "Kommentar schreiben…",
subscribedTooltip: "Abonniert — Klicken zum Abbestellen",
unsubscribedTooltip: "Benachrichtigungen abonnieren",
firstRecord: "Erster Datensatz (Pos1)",
previousRecordKey: "Vorheriger Datensatz (←)",
nextRecordKey: "Nächster Datensatz (→)",
lastRecord: "Letzter Datensatz (Ende)",
noRecords: "Keine Datensätze",
searchWhileNavigating: "Während der Navigation suchen",
searchRecords: "Datensätze suchen…",
allActivity: "Alle Aktivitäten",
commentsOnly: "Nur Kommentare",
fieldChangesFilter: "Feldänderungen",
Expand Down Expand Up @@ -1128,21 +1116,6 @@ const de = {
fileCount: "{{count}} Dateien",
fileCount_one: "{{count}} Datei",
fileCount_other: "{{count}} Dateien",
// objectui#7163 — PointInTimeRestore's revision-history chrome. The file
// used no translation hook at all, so every one of these read English in
// every session; swept in one pass rather than converting the timestamps
// alone. `Cancel`, `(empty)` and the empty-value dash reuse the keys this
// namespace already has, so only these ten are new.
revisionHistory: 'Versionsverlauf',
noRevisions: 'Keine Versionen aufgezeichnet',
revisionFieldsChanged: '{{count}} Felder geändert',
revisionFieldsChangedOne: '{{count}} Feld geändert',
revisionPreview: 'Versionsvorschau',
revisionSnapshot: 'Datensatzstand zu diesem Zeitpunkt',
restoreConfirm: 'Dadurch wird der Datensatz auf seinen Stand von {{when}} zurückgesetzt. Fortfahren?',
restoring: 'Wird wiederhergestellt…',
confirmRestore: 'Wiederherstellung bestätigen',
restoreToPoint: 'Auf diesen Zeitpunkt zurücksetzen',
},
chart: {
loading: "Diagramm wird geladen…",
Expand Down
30 changes: 1 addition & 29 deletions packages/i18n/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,6 @@ const en = {
viewAll: 'View All',
new: 'New',
add: 'Add',
emptyValue: '—',
activity: 'Activity',
history: 'History',
historyEmpty: 'No history yet',
Expand Down Expand Up @@ -1218,12 +1217,6 @@ const en = {
attachmentsLoadFailed: "We couldn't load the attachments for this record.",
attachmentsApiUnavailable: 'The attachments list is not available on this object.',
retryLoadAttachments: 'Retry',
// Diff
unifiedDiff: 'Unified diff',
sideBySideDiff: 'Side-by-side diff',
noChanges: 'No changes',
previousVersion: 'Previous',
currentVersion: 'Current',
// Discussion
discussion: 'Discussion',
showDiscussion: 'Show Discussion ({{count}})',
Expand All @@ -1241,11 +1234,7 @@ const en = {
// Subscription
subscribedTooltip: 'Subscribed — click to unsubscribe',
unsubscribedTooltip: 'Subscribe to notifications',
// Navigation
firstRecord: 'First record (Home)',
previousRecordKey: 'Previous record (←)',
nextRecordKey: 'Next record (→)',
lastRecord: 'Last record (End)',
// The reference rail's empty related-record list
noRecords: 'No records',
// objectui#3863 — the BASE key is the slot every plural category a pack did not
// enumerate resolves to, keeping that pack in its own language instead of falling
Expand All @@ -1256,8 +1245,6 @@ const en = {
showEmptyRelated: '+ {{count}} empty',
showEmptyRelated_one: '+ {{count}} empty',
showEmptyRelated_other: '+ {{count}} empty',
searchWhileNavigating: 'Search while navigating',
searchRecords: 'Search records…',
// Activity timeline
allActivity: 'All Activity',
commentsOnly: 'Comments Only',
Expand Down Expand Up @@ -1320,21 +1307,6 @@ const en = {
fileCount: '{{count}} files',
fileCount_one: '{{count}} file',
fileCount_other: '{{count}} files',
// objectui#7163 — PointInTimeRestore's revision-history chrome. The file
// used no translation hook at all, so every one of these read English in
// every session; swept in one pass rather than converting the timestamps
// alone. `Cancel`, `(empty)` and the empty-value dash reuse the keys this
// namespace already has, so only these ten are new.
revisionHistory: 'Revision History',
noRevisions: 'No revisions recorded',
revisionFieldsChanged: '{{count}} fields changed',
revisionFieldsChangedOne: '{{count}} field changed',
revisionPreview: 'Revision Preview',
revisionSnapshot: 'Record state at this point',
restoreConfirm: 'This will restore the record to its state at {{when}}. Continue?',
restoring: 'Restoring…',
confirmRestore: 'Confirm Restore',
restoreToPoint: 'Restore to this point',
},
chart: {
loading: 'Loading chart…',
Expand Down
Loading
Loading