Skip to content

Part C: API consistency - #43

Merged
n4ff4h merged 36 commits into
devfrom
plan/c-api
Nov 1, 2026
Merged

n4ff4h merged 36 commits into
devfrom
plan/c-api

Conversation

@n4ff4h

@n4ff4h n4ff4h commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator

Part C: API consistency (C1–C10).

Stacked, merge in order, and do not squash: the branches go plan/a-packaging → plan/b-bugs → plan/c-api → plan/d-ci → plan/e-docs, each built on the one before. All target dev as the plan asks, so until the earlier PRs merge this diff includes them. Review it commit by commit (one commit per plan step).

Renamed or removed APIs

Step Was Now
C2 onChange(value) on date pickers, maps, editors, InlineEdit, RepeatingGroup, LanguageSwitcher, ImageInput, AvatarUpload, AdditionalFilesUpload, TimePanel, useListingState, custom filter and bulk action render props, PublishBox renderDateInput onValueChange(value)
C2 FileInput onChange(files) removed; onValueChange(entries)
C2 onReject(rejection) on ImageInput, AvatarUpload onReject(rejections[])
C3 variant on Callout, AnnouncementBar, notification items tone
C3 default, destructive, error (also IconText tone) neutral, danger, danger
C4 size md on Tag, SkeletonText, Modal, UserAvatar default
C4 NumberInput default h-10 h-8; TextInput gains size
C6 toArray at root, initials from /media removed
C6 Callout Tiptap node from /editor CalloutNode (node name still "callout")
C6 normalizeUrl return string | null
C8 title on CommandPalette, OverlayMenu; TopNav navLabel; Tabs listLabel label
C8 minQueryLength, minLength minChars
C9 25 unused locale keys removed (list in the commit)

Added: rest props and ref (C5, seven components), classNames (C5), asChild on BackToTop, ConditionalLink, LinkButton (C5), uncontrolled partners (C7).

Worth a look

  • C2 PhoneInput's internal country select keeps onChange, because react-phone-number-input calls it.
  • C4 other md sizes were not in the plan and are unchanged: StatusBadge, IconText, Countdown, BottomSheet, SlideOver, ProgressBar, Hero, MapLegendSwatch.
  • C5 fixed a bug outside the list: Button with asChild, loading and loadingText rendered nothing.
  • C6 ApiSelect can't use useAsyncSearch (TanStack Query owns its fetching), so it keeps only the debounce. Behaviour changes: retry skips the debounce everywhere, and TextInput now leaves /page as is on blur (it used to produce https:///page). Auth forms gain PasswordInput's Caps Lock warning.
  • C7 Lightbox without index resumes at the last slide when reopened.
  • C8 OverlayMenu title="x" now compiles and lands on the root as a plain HTML attribute.
  • C9, for the maintainer: other Dhivehi strings still use inflected forms of "ފޮހެލާ", because replacing them means new wording: listing.trashEmptyDescription, date.cleared, input.signatureCleared, form.changeCleared, map.locationCleared, listing.{bulkDeleteTitle,bulkDeleted,deleteRecordTitle,deletePermanentlyTitle,deletePermanentlyDescription}, display.{deleteCommentConfirm,deleteNoteConfirm}, choice.{clearSelection,deselectAll}.
  • C10 the file-input.tsx split is skipped, as the plan allows.

Size limits at this tip

No budget changed.

Entry Budget Measured
core (Button) 13 kB 12 kB
core (FormField + TextInput) 17.5 kB 16.37 kB
core (Modal) 26 kB 24.24 kB
core (Select) 41 kB 38.38 kB
core (DataTable) 80 kB 79.04 kB
core (OnuProvider) 1.5 kB 1.25 kB
core (admin form: OnuProvider + 8 form components) 87 kB 83.53 kB
core (everything) 1.2 MB 1.1 MB
primitives 130 kB 124.12 kB
thaana 2.2 kB 1.94 kB
locales/dv 16 kB 13.24 kB
charts 48 kB 45.68 kB
maps 100 kB 97.37 kB
editor 78 kB 72.41 kB
calendar 44 kB 39.23 kB
media 54 kB 50.81 kB
export 36 kB 30.99 kB
icons/iconify 600 B 413 B
icons/fontawesome 2.1 kB 1.86 kB
styles.css (with its imports) 4.5 kB 4.42 kB
tailwind.css (with its imports) 8.5 kB 8 kB
fonts-thaana.css 400 B 327 B
fonts.css 400 B 306 B
fonts-optional.css 300 B 211 B

Checks

At this tip: typecheck, 2697 unit tests, size pass. Lint, format and docs check passed on each commit. build:web and template typecheck passed at C10.

🤖 Generated with Claude Code

n4ff4h and others added 30 commits November 1, 2026 10:41
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The changesets fixed group keeps @javaabu/onu and @javaabu/onu-mcp together, and
scripts/sync-plugin-version.mjs keeps the plugin manifest in step; the release
workflow fails if it differs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The @font-face rules move to fonts-thaana.css, imported by tailwind.css, styles.css and
fonts.css. New budget: fonts-thaana.css 400 B (327 B measured). tailwind.css stays within
8.5 kB (7.97 kB) and styles.css within 4.5 kB (4.38 kB). fonts.css drops to 400 B now the
font faces moved out (306 B).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both share React context with the app, so a second copy silently disconnects them.
@tanstack/react-query is a required peer, not optional as first planned: the core entry
imports ApiSelect statically, so an app without the package would fail to build.

The fallback QueryClient moves from a module singleton (shared across SSR requests) to
each OnuProvider, or the component when there is no provider. getOnuQueryClient is gone,
and invalidateApiOptions takes the client first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing in src, web or templates used it; every Onu select is built on cmdk. The primitives budget drops from 165 kB to 130 kB (124.25 kB measured).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Chevron renders an Icon that already mirrors, so the rotate-180 on top turned the arrows back to their LTR direction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A caller's data-slot replaced Button's, so the 44 px rule in base.css no longer matched. Button now always emits data-slot=button, and the 34 callers that named their button (27 Button, 7 InputGroupButton) use data-part. The plan listed 7; the rest had the same bug.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The module-level confirm() has no locale, so it keeps the English default; useConfirm uses the provider's. ValidationError.message is now empty when no message is given, so ValidationSummary shows form.errorSummaryTitle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The link and embed dialogs only have URL fields, so the alt text field is the only free-text field in the editor dialogs. The dialogs are rendered by the editors, not the toolbar, so the prop goes straight there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New keys: listing.moveItemMenu, moveItemUp, moveItemDown, choice.switchAccountFrom, form.editValue. Removed: listing.moveUp, moveDown. listing.moveTo stays as the menu's heading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The button is rotated 90 degrees, so a mirrored chevron pointed the wrong way along the vertical axis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on a span

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
useNow starts from null and ticks on mount. RelativeDate also calls useNow, so it gets the same placeholder (the exact date). PublishBox now shares useNow instead of its own Date.now() state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--onu-overlay keeps today's look (black at 10%) in both modes. App sidebar and the docs nav had the same hex fallbacks as _shell.ts and app-header.tsx, so they change too. figma/tokens-studio.json and the plugin are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces six copies. Tabs, Wizard, Callout, FilterPanel and PasswordInput move to the hook too, so null is no longer read as uncontrolled there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
onChange now only means the native event. The renamed props: every date picker, map, editor, inline and repeating form control, LanguageSwitcher, ImageInput, AvatarUpload, AdditionalFilesUpload, TimePanel, useListingState, the custom filter and bulk action render props, and the map adapter shapes. PhoneInput's internal country select keeps onChange, because react-phone-number-input calls it. FileInput drops onChange(files). onReject takes an array everywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
md becomes default on the four components named in the plan. AvatarUpload's keys already match UserAvatar's once that happens. NumberInput is h-8 at default with smaller stepper buttons, and TextInput gets the same size prop (h-7, h-8, h-10). Other md sizes (StatusBadge, IconText, Countdown, BottomSheet, SlideOver, ProgressBar, Hero, MapLegendSwatch) were not in the plan and are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven components now spread rest props and ref on their root, the multi-part components gain classNames slot maps, and asChild is added to BackToTop, ConditionalLink and LinkButton. Button keeps loadingText inside an asChild child, so SubmitButton asChild works while processing. The Definition of Done now asks for asChild only where an app may need to replace the one rendered element.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New modules: lib/initials, lib/url, hooks/use-copy-to-clipboard, use-merged-ref, use-async-search; toArray moves to lib/utils.

Root exports: toArray is removed. getInitials stays because docs/display-and-feedback/user-avatar.md documents it; the media entry's initials export is removed. normalizeUrl now returns string | null; TextInput keeps its own handling of //host and unsafe values on blur.

useAsyncSearch serves Autocomplete/TagsInput, CommandPalette and PlaceSearchBox. ApiSelect does not fit: TanStack Query owns its fetching, cancelling and caching, so it keeps only the debounced value, useDebouncedValue, now in the same module with the same 300 ms default. Retry now skips the debounce at every site (it already did in PlaceSearchBox). The form's filter(Boolean) stays at the form call sites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LanguageSwitcher and RepeatingGroup gain defaultValue; Callout, FilterPanel and Lightbox gain defaultOpen. Lightbox open and onOpenChange are optional, and index is controlled only when passed. Uncontrolled, Lightbox tracks its own slide, so reopening resumes where it was instead of at the first slide. LanguageSwitcher without value or defaultValue still follows the provider's locale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CommandPalette and OverlayMenu title were only ever sr-only accessible names, so they become label with no visible change. TopNav navLabel and Tabs listLabel become label; CommandPalette minQueryLength and PlaceSearchBox/usePlaceSearchBox minLength become minChars. MapView, Carousel, ButtonGroup, Autocomplete and ApiSelect already used these names. AppSidebar navLabel is not in the plan's list and is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Delete unused keys from en and dv after checking literal and dynamic use: common.{ok,clearAll,apply,edit,view,search,remove,add,open,menu,error,success,warning,info}, overlay.openCommandPalette, chart.typeSparkline, layout.{footer,moreActions}, display.readMore, choice.{options,rating}, listing.{sortByColumn,sortDirection,sortNone,firstPage,lastPage,deletedRecords,tabWithCount,exportAs}. No candidate turned out to be reached dynamically.

Wire listing.filtersApplied through useAnnouncer in FilterPanel and listing.selectionCleared in DataTable, with tests. Drop the stale export and comment from media/index.ts.

Dhivehi: common.clear, date.clearTime and file.clearSelection now use the clear word; common.delete, editor.{deleteRow,deleteColumn,deleteTable,deleteBlock}, display.{deleteComment,deleteNote} and listing.deletePermanently use the delete word. input.signatureClear keeps its erase word.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure move of components/form/auth-forms.tsx into sign-in-form, register-form, forgot-password-form and reset-password-form, with the shared shell in _auth-shell. Tests split the same way. Root exports are unchanged; the auth forms docs page covers the new files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pnpm 12 keeps the version from packageManager in a section of pnpm-lock.yaml, and a frozen install fails if it is missing. Moving to 12.16.1 in 15ccf53 changed package.json only, so every CI job has failed at pnpm install since. It didn't show locally because an older global pnpm that switches itself to 12.16.1 doesn't check this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
n4ff4h and others added 6 commits November 1, 2026 14:09
next 16.4.3 (critical RCE and high DoS, templates/nextjs). shadcn 4.23.0, a dev dependency, which moves its zod to 4.6.7. In the lockfile, within existing ranges: body-parser 2.4.0, qs 6.17.1, fast-uri 3.1.9, ip-address 10.7.5, undici 7.30.1 and 8.12.3, systeminformation 5.33.19. pnpm audit --prod is clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The open dialog's focus timer ran after the editor unmounted, and vitest's unhandled error made test:coverage exit 1. D4 has the same fix; this brings it down to where the test was added, so #42 and #43 pass on their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@n4ff4h
n4ff4h merged commit dbb162d into dev Nov 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant