From a20fde112b8a067bc6eb6d43bc3c6eb9562890f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 20 Sep 2026 00:16:04 +0000 Subject: [PATCH 1/5] feat(spec): declare the author-settable row ceiling on the page-shaped view configs `GalleryConfigSchema`, `KanbanConfigSchema` and `TimelineConfigSchema` each gain a `limit` member: an int-positive row ceiling with the default applied (100), whose describe states that default and the visible truncation signal the renderer owes when the ceiling applies. `DEFAULT_VIEW_ROW_LIMIT` is exported so a consumer reads the number instead of re-declaring it. The name and the placement are the protocol absorbing keys the consumers already read: objectui caps kanban (`$top: schema.limit ?? DEFAULT_KANBAN_LIMIT`) and timeline off keys declared in `@object-ui/types` and on a component props interface, never in the protocol. The non-grid four keep their platform ceiling and gain nothing here. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- packages/spec/src/ui/view.test.ts | 111 ++++++++++++++++++++++++++++++ packages/spec/src/ui/view.zod.ts | 96 ++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) diff --git a/packages/spec/src/ui/view.test.ts b/packages/spec/src/ui/view.test.ts index 44f98706e4c..0047dc4745a 100644 --- a/packages/spec/src/ui/view.test.ts +++ b/packages/spec/src/ui/view.test.ts @@ -44,6 +44,8 @@ import { ViewItemSchema, ViewMetadataSchema, VIEW_METADATA_MEMBERS, + TreeConfigSchema, + DEFAULT_VIEW_ROW_LIMIT, } from './view.zod'; import { @@ -4660,3 +4662,112 @@ describe('ListViewSchema — `viewType` is not a spelling of `type` (#16577)', ( expect((r as { data: { type?: unknown } }).data.type).toBe('grid'); }); }); + + +// ============================================================================ +// [#17393] The author-settable row ceiling on the page-shaped view configs. +// +// A protocol-first card: objectui caps kanban and timeline by author choice off +// a key `@objectstack/spec` never declared (`$top: schema.limit ?? DEFAULT_*_LIMIT`), +// and the gallery — the third page-shaped view — caps not at all. These pins +// hold the new declaration to the three things a ceiling has to be: APPLIED +// (the default the prose states is the default the parse produces), BOUNDED +// (a value that could not cap a fetch is refused by name), and SCOPED (the +// non-grid four keep objectui#7210's platform ceiling and do not gain an +// authorable one). +// ============================================================================ + +describe('view row ceiling — `limit` on the page-shaped view configs (#17393)', () => { + /** + * One minimal, parse-clean block per page-shaped config, so every verdict + * below is about `limit` alone rather than about a missing sibling key. + */ + const PAGE_SHAPED = [ + ['gallery', GalleryConfigSchema as unknown as z.ZodTypeAny, {}], + ['kanban', KanbanConfigSchema as unknown as z.ZodTypeAny, { groupByField: 'status', columns: ['name'] }], + ['timeline', TimelineConfigSchema as unknown as z.ZodTypeAny, { startDateField: 'start_date', titleField: 'name' }], + ] as const; + + /** The `limit` member's own `.describe()` text, off the built shape. */ + const describeOf = (schema: z.ZodTypeAny): string => + (schema as unknown as { shape: Record }).shape.limit?.description ?? ''; + + it('applies the ceiling it declares when the author writes none', () => { + for (const [label, schema, minimal] of PAGE_SHAPED) { + const parsed = schema.parse({ ...minimal }) as { limit?: unknown }; + expect(parsed.limit, label).toBe(DEFAULT_VIEW_ROW_LIMIT); + } + }); + + it('accepts an authored ceiling as a MEMBER, with both controls firing on the same shape', () => { + for (const [label, schema, minimal] of PAGE_SHAPED) { + // CONTROL-1 — this surface CAN refuse a key, so acceptance below means something. + const control = schema.safeParse({ ...minimal, zzUnlikelyBogusKey__: 7 }); + expect(control.success, label).toBe(false); + expect(JSON.stringify((control as { error?: z.ZodError }).error?.issues), label) + .toContain('unrecognized_keys'); + + // CONTROL-2 — the refusal is about the NAME: the same block without it parses. + expect(schema.safeParse({ ...minimal }).success, label).toBe(true); + + // PROBE — the authored value SURVIVES the parse; it is not merely tolerated. + const probe = schema.safeParse({ ...minimal, limit: 25 }); + expect(probe.success, label).toBe(true); + expect(((probe as { data?: { limit?: unknown } }).data)?.limit, label).toBe(25); + } + }); + + it('refuses a value that could not bound a fetch — and refuses it BY NAME', () => { + for (const [label, schema, minimal] of PAGE_SHAPED) { + for (const bad of [0, -1, 2.5, '100', null] as const) { + const at = `${label} limit=${JSON.stringify(bad)}`; + const result = schema.safeParse({ ...minimal, limit: bad }); + expect(result.success, at).toBe(false); + const issues = (result as { error?: z.ZodError }).error?.issues ?? []; + expect(issues.some((issue) => issue.path[0] === 'limit'), `${at}: ${JSON.stringify(issues)}`) + .toBe(true); + } + } + }); + + it('states the default it ACTUALLY applies — prose and schema pinned to each other', () => { + for (const [label, schema, minimal] of PAGE_SHAPED) { + const description = describeOf(schema); + const stated = /default (\d+)/.exec(description); + expect(stated, `${label}: ${description}`).not.toBeNull(); + const applied = (schema.parse({ ...minimal }) as { limit: number }).limit; + expect(Number(stated?.[1]), `${label}: ${description}`).toBe(applied); + } + }); + + it('tells the author the renderer owes a VISIBLE truncation signal', () => { + // ⛔ The signal itself is the renderer's half and cannot be enforced from a + // schema. What the protocol can do — and what objectui#7390's ruling turns + // on — is say the cap is owed a signal, so that "bounded and silent" is + // never read as the finished job. + for (const [label, schema] of PAGE_SHAPED) { + expect(describeOf(schema), label).toContain('visible truncation signal'); + } + }); + + it('leaves the non-grid four WITHOUT an authorable ceiling (objectui#7210 keeps theirs)', () => { + // The card scopes those four out by name: their rows are capped by a + // platform constant the renderer owns, because a gantt range, a map camera + // fit and a tree parent chain are computed over the whole set. An + // authorable ceiling there would be surface no renderer reads. + const NON_GRID_FOUR = [ + ['gantt', GanttConfigSchema as unknown as z.ZodTypeAny], + ['calendar', CalendarConfigSchema as unknown as z.ZodTypeAny], + ['map', ListMapConfigSchema as unknown as z.ZodTypeAny], + ['tree', TreeConfigSchema as unknown as z.ZodTypeAny], + ] as const; + + for (const [label, schema] of NON_GRID_FOUR) { + const result = schema.safeParse({ limit: 10 }); + expect(result.success, label).toBe(false); + const unrecognized = ((result as { error?: z.ZodError }).error?.issues ?? []) + .find((issue) => issue.code === 'unrecognized_keys'); + expect(JSON.stringify(unrecognized), label).toContain('limit'); + } + }); +}); diff --git a/packages/spec/src/ui/view.zod.ts b/packages/spec/src/ui/view.zod.ts index 358aed5fc71..108ba105c06 100644 --- a/packages/spec/src/ui/view.zod.ts +++ b/packages/spec/src/ui/view.zod.ts @@ -1128,6 +1128,99 @@ export const GroupingConfigSchema = lazySchema(() => strictObject({ + 'AND-ed into the view filter. Compiled by `compileListViewGroupQuery` / `compileListViewGroupRowsQuery`', )); +/* + * --------------------------------------------------------------------------- + * `limit` — the author-settable row ceiling of the page-shaped views (#17393) + * --------------------------------------------------------------------------- + * + * Declared here because the PROTOCOL was the thing that was wrong: two + * renderers already cap by author choice, and the key they read was never a + * protocol key. Measured in objectui at `dda8f3815d`: + * + * - `ObjectKanban.tsx:573` fetches `$top: schema.limit ?? DEFAULT_KANBAN_LIMIT` + * (`= 100` at `:84`), and that `limit` is declared in `@object-ui/types` + * alone (`zod/objectql.zod.ts:1762`, `z.number().int().positive().optional()`); + * - `ObjectTimeline.tsx:328` fetches `$top: schema.limit ?? DEFAULT_TIMELINE_LIMIT` + * (`= 100` at `:29`), with `limit` on that component's own props interface + * (`:129`) and on no published schema at all; + * - `ObjectGallery.tsx` sends no `$top` and reads no ceiling at all — the + * unbounded fetch objectui#7390 is ruled to close by reading this key. + * + * Consumer-local author-settable keys the protocol never declared are the + * divergence the contract-first directive forbids, so the knob enters the + * protocol first and the three spellings come under one declaration (the + * director seat's amendment of 2026-09-10T11:0xZ on objectui#7390, on the + * maintainer's principle 「我们的项目以objectstack 协议为准,文档应该以实际实现 + * 为准。协议不正确的应该先修改协议。」). + * + * ## Why on the per-view config blocks, and not as a member of the list view + * + * The alternative shape — one row ceiling on {@link ListViewShapeSchema} + * itself — is rejected on three properties of this tree: + * + * 1. The base shape ALREADY carries the row-bounding knob every view type + * reaches: `pagination.pageSize` ({@link PaginationConfigSchema}, default + * 25). A second base-level row key would leave one view with two + * base-level row bounds and no declared precedence between them — and the + * `virtualScroll` tombstone at the bottom of this same shape prescribes + * `pagination` for exactly that question. + * 2. A base member is reachable from EVERY `type`, the non-grid four + * (gantt / calendar / map / tree) included. Their ceiling is a platform + * constant the renderer owns (objectui#7210) and this card does not touch + * them, so a base member would publish an authorable ceiling on four view + * kinds no renderer reads — declared-but-unenforced on the day it lands. + * 3. The per-kind block is what actually REACHES the renderer: objectui's + * `ListView` merges `schema.` into the generated node — its kanban + * branch spreads the rest of the block flat onto `object-kanban`, so + * `kanban.limit` lands exactly where `schema.limit` is read — while a + * base-level key is forwarded into no per-kind node at all. + * + * The NAME is `limit` for the same reason: it is the name the consumer already + * reads, so this declaration absorbs the two consumer-local keys instead of + * buying a second divergence spelled differently. + * + * ⚠️ NOT the kanban LANE's `limit`. objectui's node-level + * `ObjectKanbanLaneSchema.limit` is a WIP warning threshold that never reaches + * a query; no lane object exists on this face at all + * ({@link KanbanConfigSchema}'s `columns` is a list of card FIELD names), so + * the two cannot be confused here. + */ +export const DEFAULT_VIEW_ROW_LIMIT = 100; + +/** What a page-shaped view's ceiling bounds, per view type. */ +const ROW_LIMIT_SUBJECT = { + gallery: 'cards the gallery fetches and draws', + kanban: 'records the board fetches across all its lanes', + timeline: 'rows the timeline fetches onto its rail', +} as const; + +/** The three view configs that cap by AUTHOR choice (not by platform ceiling). */ +type RowLimitView = keyof typeof ROW_LIMIT_SUBJECT; + +/** + * The `limit` declaration for one page-shaped view config. + * + * The default is APPLIED, not merely described: a `.describe()` naming a + * default the schema does not apply is a second contract that nothing + * enforces, and the two drift the first time either is edited. The agreement + * is pinned from both sides in `view.test.ts` (#17393) — the parsed default is + * compared against the number the describe text states. + * + * ⛔ The truncation signal is the renderer's half and cannot be enforced from + * here; it is stated in the describe because a bounded-and-silent view reads + * as complete, which is worse than the unbounded-and-silent one this key + * replaces — the author needs to know the cap is visible, and the renderer + * author needs to know it is owed. + */ +const rowLimitKey = (view: RowLimitView) => + z.number().int().positive().default(DEFAULT_VIEW_ROW_LIMIT).describe( + `Row ceiling — the most ${ROW_LIMIT_SUBJECT[view]}, sent as the query \`$top\`; default ` + + `${DEFAULT_VIEW_ROW_LIMIT} when the key is absent. When the ceiling APPLIES (the filtered ` + + 'set is larger than it), the renderer must show a visible truncation signal saying what is ' + + 'on screen is not the whole set — a bounded view that looks complete is worse than an ' + + 'unbounded one.', + ); + /** * Gallery View Configuration (Airtable-style) * Configures card layout for gallery/card views. @@ -1141,6 +1234,7 @@ export const GalleryConfigSchema = lazySchema(() => strictObject({ cardSize: z.enum(['small', 'medium', 'large']).default('medium').describe('Card size in gallery view'), titleField: z.string().optional().describe('Field to display as card title'), visibleFields: z.array(z.string()).optional().describe('Fields to display on card body'), + limit: rowLimitKey('gallery'), }).describe('Gallery/card view configuration')); /** @@ -1163,6 +1257,7 @@ export const TimelineConfigSchema = lazySchema(() => strictObject({ ), colorField: z.string().optional().describe('Field to derive each item color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the timeline default marker color'), scale: z.enum(['hour', 'day', 'week', 'month', 'quarter', 'year']).default('week').describe('Default timeline scale'), + limit: rowLimitKey('timeline'), }).describe('Timeline view configuration')); /** @@ -1466,6 +1561,7 @@ export const KanbanConfigSchema = lazySchema(() => strictObject({ */ titleField: z.string().optional().describe('Field displayed as the card title. Omit to fall back to the record display name (ADR-0079 resolver chain)'), columns: z.array(z.string()).describe('Fields to show on cards'), + limit: rowLimitKey('kanban'), })); /** From 38c4c2bfd172a2e2887e6cbe234c3196d63c7af5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 20 Sep 2026 00:26:33 +0000 Subject: [PATCH 2/5] chore(spec): declare KanbanConfigParsed and move the ADR-0122 pin count The applied default on `KanbanConfigSchema.limit` gives that schema a second shape, which is the event `type-alias-convention.pin.test.ts` exists to catch: its `Iso829` pin leaves, `KanbanConfigParsed` is declared beside the bare alias as ADR-0122 prescribes, and the pin count plus both prose statements of it move 785 -> 784. Also carries the regenerated authorable-surface and authorable-defaults rows the three new keys add. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- packages/spec/authorable-defaults/ui.json | 3 +++ packages/spec/authorable-surface/ui.json | 3 +++ .../src/type-alias-convention.pin.test.ts | 24 +++++++++++++++---- packages/spec/src/ui/view.zod.ts | 2 ++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/spec/authorable-defaults/ui.json b/packages/spec/authorable-defaults/ui.json index bf6b528ec8b..8a89d4c6b88 100644 --- a/packages/spec/authorable-defaults/ui.json +++ b/packages/spec/authorable-defaults/ui.json @@ -48,6 +48,7 @@ "ui/FormView:type = \"simple\"", "ui/GalleryConfig:cardSize = \"medium\"", "ui/GalleryConfig:coverFit = \"cover\"", + "ui/GalleryConfig:limit = 100", "ui/GlobalFilter:scope = \"dashboard\"", "ui/GroupNavItem:expanded = false", "ui/GroupingField:collapsed = false", @@ -56,6 +57,7 @@ "ui/InlineAction:refreshAfter = false", "ui/InlineAction:type = \"script\"", "ui/JoinedReportBlock:type = \"tabular\"", + "ui/KanbanConfig:limit = 100", "ui/ListChartConfig:chartType = \"bar\"", "ui/ListView:type = \"grid\"", "ui/NavigationConfig:mode = \"page\"", @@ -104,6 +106,7 @@ "ui/SelectionConfig:type = \"none\"", "ui/SharingConfig:allowAnonymous = false", "ui/SharingConfig:enabled = false", + "ui/TimelineConfig:limit = 100", "ui/TimelineConfig:scale = \"week\"", "ui/UrlNavItem:target = \"_self\"", "ui/UserActionsConfig:addRecordForm = false", diff --git a/packages/spec/authorable-surface/ui.json b/packages/spec/authorable-surface/ui.json index 5c436c98a5b..0510caa7720 100644 --- a/packages/spec/authorable-surface/ui.json +++ b/packages/spec/authorable-surface/ui.json @@ -486,6 +486,7 @@ "ui/GalleryConfig:cardSize", "ui/GalleryConfig:coverField", "ui/GalleryConfig:coverFit", + "ui/GalleryConfig:limit", "ui/GalleryConfig:titleField", "ui/GalleryConfig:visibleFields", "ui/GanttConfig:assigneeField", @@ -595,6 +596,7 @@ "ui/JoinedReportBlock:values", "ui/KanbanConfig:columns", "ui/KanbanConfig:groupByField", + "ui/KanbanConfig:limit", "ui/KanbanConfig:summarizeField", "ui/KanbanConfig:titleField", "ui/ListChartConfig:chartType", @@ -1151,6 +1153,7 @@ "ui/TimelineConfig:colorField", "ui/TimelineConfig:endDateField", "ui/TimelineConfig:groupByField", + "ui/TimelineConfig:limit", "ui/TimelineConfig:scale", "ui/TimelineConfig:startDateField", "ui/TimelineConfig:titleField", diff --git a/packages/spec/src/type-alias-convention.pin.test.ts b/packages/spec/src/type-alias-convention.pin.test.ts index 8ad36018f81..276d3fc3460 100644 --- a/packages/spec/src/type-alias-convention.pin.test.ts +++ b/packages/spec/src/type-alias-convention.pin.test.ts @@ -275,7 +275,7 @@ import type * as M187 from './shared/duration.zod.js'; import type * as M188 from './ai/build-progress.zod.js'; // --------------------------------------------------------------------------- -// 785 isomorphic aliases: `z.input` === `z.infer`, so no `XParsed` is declared. +// 784 isomorphic aliases: `z.input` === `z.infer`, so no `XParsed` is declared. // // That number is machine-checked, not hand-kept. The runtime companion at the // bottom of this file recomputes the pin count from the source and asserts that @@ -1599,7 +1599,12 @@ export type Iso823 = Assert, z.infer< type export type Iso826 = Assert, z.infer< typeof M167.CalendarConfigSchema > >>; export type Iso827 = Assert, z.infer< typeof M167.GanttConfigSchema > >>; export type Iso828 = Assert, z.infer< typeof M167.GanttQuickFilterSchema > >>; -export type Iso829 = Assert, z.infer< typeof M167.KanbanConfigSchema > >>; +// (Iso829 `KanbanConfigSchema` left this list in #17393: the author-settable +// row ceiling `limit` APPLIES its default, which is exactly the "a nested field +// gains a `.default()`" event this file exists to catch — so the schema now has +// two shapes and `KanbanConfigParsed` is declared beside the bare alias, as +// ADR-0122 prescribes. Its two page-shaped siblings needed no line moved: both +// already carried defaults and therefore both halves of the pair.) export type Iso851 = Assert, z.infer< typeof M167.ListMapConfigSchema > >>; export type Iso830 = Assert, z.infer< typeof M167.NavigationModeSchema > >>; export type Iso831 = Assert, z.infer< typeof M167.TreeConfigSchema > >>; @@ -1678,7 +1683,7 @@ describe('ADR-0122 type-alias convention', () => { // this title and the section header above the pin list — are now asserted // against the recomputed count below, so neither can go stale without a red // test naming it. - it('still declares all 785 isomorphic pins', () => { + it('still declares all 784 isomorphic pins', () => { // The truth of each pin is proved by tsc, not here — an `Assert>` // that stops holding is a compile error with the alias named. What tsc // cannot notice is a pin that was DELETED: removing the assertion removes @@ -2252,7 +2257,18 @@ describe('ADR-0122 type-alias convention', () => { // Note the number is the same 783 -> 785 the DURATION block above records, // arrived at from the same 783 after #16059's retirement took it back down. // The two entries are different movements that share a pair of endpoints. - expect(pins).toHaveLength(785); + // 785 -> 784 is #17393's author-settable row ceiling on the page-shaped + // view configs (ui/view.zod.ts, module slot M167): `KanbanConfigSchema` + // gained a `limit` member whose default is APPLIED, which is the "a nested + // field gains a `.default()`" event at the top of this file, one level in. + // Author state and parsed state part company, so the pin left and + // `KanbanConfigParsed` is declared beside the bare alias. Its two siblings + // in that card moved no line: `GalleryConfig` and `TimelineConfig` already + // carried defaults (`coverFit` / `cardSize`, `scale`) and therefore already + // carried both halves of the pair — which is also why only ONE of the three + // was ever on this list. -1 converted to an `XParsed` pair; the Iso number + // stays vacant (ids are claims about pins, not positions). + expect(pins).toHaveLength(784); // The count is stated in PROSE twice as well — this case's title and the // section header above the pin list — and until #6605 nothing read either diff --git a/packages/spec/src/ui/view.zod.ts b/packages/spec/src/ui/view.zod.ts index 108ba105c06..65853f52f3b 100644 --- a/packages/spec/src/ui/view.zod.ts +++ b/packages/spec/src/ui/view.zod.ts @@ -5933,6 +5933,8 @@ export type CalendarConfig = z.input; export type GanttConfig = z.input; export type GanttQuickFilter = z.input; export type KanbanConfig = z.input; +/** Post-parse shape of {@link KanbanConfig} — defaults applied, transforms run (ADR-0122). */ +export type KanbanConfigParsed = z.infer; export type ListMapConfig = z.input; export type NavigationMode = z.input; export type TreeConfig = z.input; From 16b54be1fadece9875b7edc58e8801985d291984 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 20 Sep 2026 00:33:49 +0000 Subject: [PATCH 3/5] chore(spec): regenerate the artifacts the row-ceiling keys move, add the changeset api-surface, api-surface-declarations, export-origins and the docs references tree, regenerated with the repo's own tooling after a real (non-OS_SKIP_DTS) build. The four non-ui declaration files move only where the view schema is embedded in them. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- .changeset/17393-view-row-ceiling.md | 36 +++++++ content/docs/references/api/protocol.mdx | 4 +- content/docs/references/data/object.mdx | 2 +- content/docs/references/ui/view.mdx | 21 ++-- .../spec/api-surface-declarations/api.txt | 24 +++++ .../spec/api-surface-declarations/data.txt | 9 ++ .../spec/api-surface-declarations/root.txt | 48 ++++++++++ .../spec/api-surface-declarations/system.txt | 96 +++++++++++++++++++ packages/spec/api-surface-declarations/ui.txt | 34 ++++++- packages/spec/api-surface/ui.json | 2 + packages/spec/export-origins/ui.json | 2 + 11 files changed, 267 insertions(+), 11 deletions(-) create mode 100644 .changeset/17393-view-row-ceiling.md diff --git a/.changeset/17393-view-row-ceiling.md b/.changeset/17393-view-row-ceiling.md new file mode 100644 index 00000000000..675b2c05b0f --- /dev/null +++ b/.changeset/17393-view-row-ceiling.md @@ -0,0 +1,36 @@ +--- +'@objectstack/spec': minor +--- + +Gallery, kanban and timeline view configs declare an author-settable row ceiling. + +`GalleryConfigSchema`, `KanbanConfigSchema` and `TimelineConfigSchema` each gain a +`limit` member — a positive integer, default **100** — saying how many records the +view fetches. The default is APPLIED by the schema rather than only described, and +the key's own text states the other half of the contract: when the ceiling applies, +the renderer must show a visible truncation signal, because a bounded view that +looks complete is worse than an unbounded one. `DEFAULT_VIEW_ROW_LIMIT` is exported +so a consumer reads that number instead of re-declaring it. + +The knob belongs in the protocol because two renderers already cap by author choice +off keys the protocol never declared: objectui's kanban board fetches +`$top: schema.limit ?? DEFAULT_KANBAN_LIMIT` with `limit` declared in +`@object-ui/types` alone, its timeline does the same off a component props +interface, and its gallery caps not at all. `limit` is the name those consumers +already read, so this declaration absorbs the consumer-local keys instead of +introducing a second spelling of one concept. + +Nothing is removed, renamed or narrowed, and no document that parsed before is +refused now. Two things to know when upgrading: + +- a parsed gallery / kanban / timeline config carries `limit: 100` where the author + wrote no ceiling, so code that compares a parsed config against a literal object + sees the new member; +- `KanbanConfigParsed` is now declared (ADR-0122) because that schema has two shapes + for the first time; `KanbanConfig` is unchanged and remains the author state. + +The non-grid four — gantt, calendar, map and tree — are deliberately untouched: +their rows stay bounded by a platform ceiling the renderer owns, because a gantt's +range, a map's camera fit and a tree's parent chain are computed over the whole set. + +Clause-②: yes (widening) diff --git a/content/docs/references/api/protocol.mdx b/content/docs/references/api/protocol.mdx index 008b1df0061..921b6dd49ed 100644 --- a/content/docs/references/api/protocol.mdx +++ b/content/docs/references/api/protocol.mdx @@ -1674,7 +1674,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -1759,7 +1759,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | diff --git a/content/docs/references/data/object.mdx b/content/docs/references/data/object.mdx index e241f90d292..cde88732c5c 100644 --- a/content/docs/references/data/object.mdx +++ b/content/docs/references/data/object.mdx @@ -374,7 +374,7 @@ const result = ApiMethod.parse(data); | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | diff --git a/content/docs/references/ui/view.mdx b/content/docs/references/ui/view.mdx index 00d53fa03c0..32acc8aaa29 100644 --- a/content/docs/references/ui/view.mdx +++ b/content/docs/references/ui/view.mdx @@ -545,6 +545,7 @@ Gallery/card view configuration | **cardSize** | `Enum<'small' \| 'medium' \| 'large'>` | optional (default: `"medium"`) | Card size in gallery view | | **titleField** | `string` | optional | Field to display as card title | | **visibleFields** | `string[]` | optional | Fields to display on card body | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most cards the gallery fetches and draws, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | --- @@ -700,6 +701,7 @@ HTTP methods a view data source may request — the subset of `HttpMethod` witho | **summarizeField** | `string` | optional | Field to sum at top of column (e.g. amount) | | **titleField** | `string` | optional | Field displayed as the card title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **columns** | `string[]` | ✅ | Fields to show on cards | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most records the board fetches across all its lanes, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | --- @@ -801,7 +803,7 @@ Map view configuration | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -938,6 +940,7 @@ View filter rule | **summarizeField** | `string` | optional | Field to sum at top of column (e.g. amount) | | **titleField** | `string` | optional | Field displayed as the card title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **columns** | `string[]` | ✅ | Fields to show on cards | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most records the board fetches across all its lanes, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | ### Nested Shape: `ListView.calendar` @@ -992,6 +995,7 @@ View filter rule | **cardSize** | `Enum<'small' \| 'medium' \| 'large'>` | optional (default: `"medium"`) | Card size in gallery view | | **titleField** | `string` | optional | Field to display as card title | | **visibleFields** | `string[]` | optional | Fields to display on card body | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most cards the gallery fetches and draws, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | ### Nested Shape: `ListView.timeline` @@ -1003,6 +1007,7 @@ View filter rule | **groupByField** | `string` | optional | Field to group timeline rows. NO leading or trailing whitespace: the renderer reads this name off every row verbatim, so a padded spelling drops every row into one ungrouped band. | | **colorField** | `string` | optional | Field to derive each item color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the timeline default marker color | | **scale** | `Enum<'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>` | optional (default: `"week"`) | Default timeline scale | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most rows the timeline fetches onto its rail, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | ### Nested Shape: `ListView.chart` @@ -1209,7 +1214,7 @@ Tab configuration for multi-tab view interface | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -1337,6 +1342,7 @@ View filter rule | **summarizeField** | `string` | optional | Field to sum at top of column (e.g. amount) | | **titleField** | `string` | optional | Field displayed as the card title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **columns** | `string[]` | ✅ | Fields to show on cards | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most records the board fetches across all its lanes, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | ### Nested Shape: `ObjectListView.calendar` @@ -1391,6 +1397,7 @@ View filter rule | **cardSize** | `Enum<'small' \| 'medium' \| 'large'>` | optional (default: `"medium"`) | Card size in gallery view | | **titleField** | `string` | optional | Field to display as card title | | **visibleFields** | `string[]` | optional | Fields to display on card body | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most cards the gallery fetches and draws, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | ### Nested Shape: `ObjectListView.timeline` @@ -1402,6 +1409,7 @@ View filter rule | **groupByField** | `string` | optional | Field to group timeline rows. NO leading or trailing whitespace: the renderer reads this name off every row verbatim, so a padded spelling drops every row into one ungrouped band. | | **colorField** | `string` | optional | Field to derive each item color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the timeline default marker color | | **scale** | `Enum<'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>` | optional (default: `"week"`) | Default timeline scale | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most rows the timeline fetches onto its rail, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | ### Nested Shape: `ObjectListView.chart` @@ -1655,6 +1663,7 @@ Timeline view configuration | **groupByField** | `string` | optional | Field to group timeline rows. NO leading or trailing whitespace: the renderer reads this name off every row verbatim, so a padded spelling drops every row into one ungrouped band. | | **colorField** | `string` | optional | Field to derive each item color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the timeline default marker color | | **scale** | `Enum<'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>` | optional (default: `"week"`) | Default timeline scale | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most rows the timeline fetches onto its rail, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | --- @@ -1808,7 +1817,7 @@ Tab configuration for multi-tab view interface | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -1893,7 +1902,7 @@ Tab configuration for multi-tab view interface | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -2134,7 +2143,7 @@ This schema accepts one of the following structures: | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -2310,7 +2319,7 @@ This schema accepts one of the following structures: | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[]; … }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | diff --git a/packages/spec/api-surface-declarations/api.txt b/packages/spec/api-surface-declarations/api.txt index a24ecbade42..9347bcb8083 100644 --- a/packages/spec/api-surface-declarations/api.txt +++ b/packages/spec/api-surface-declarations/api.txt @@ -10080,6 +10080,7 @@ declare const GetUiViewResponseSchema: z.ZodObject<{ summarizeField: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -10174,6 +10176,7 @@ declare const GetUiViewResponseSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -11412,6 +11417,7 @@ declare const GetUiViewResponseSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -17991,6 +17999,7 @@ declare const ObjectDefinitionResponseSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -25184,6 +25207,7 @@ declare const ObjectDefinitionResponseSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -3170,6 +3172,7 @@ declare const ChangeSetSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -10363,6 +10380,7 @@ declare const ChangeSetSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -13362,6 +13382,7 @@ declare const ChangeSetSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -20555,6 +20590,7 @@ declare const ChangeSetSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -23517,6 +23555,7 @@ declare const CreateObjectOperation: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -30710,6 +30763,7 @@ declare const CreateObjectOperation: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -34456,6 +34512,7 @@ declare const EnvironmentArtifactSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -41649,6 +41720,7 @@ declare const EnvironmentArtifactSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -44069,6 +44143,7 @@ declare const EnvironmentArtifactSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -45307,6 +45384,7 @@ declare const EnvironmentArtifactSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -58724,6 +58804,7 @@ declare const MigrationOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; gallery: z.ZodOptional; @@ -65917,6 +66012,7 @@ declare const MigrationOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional) => Dashboard; @@ -8606,6 +8609,7 @@ declare const GalleryConfigSchema: z.ZodObject<{ }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>; // ── GanttConfig (type) ── @@ -9549,12 +9553,16 @@ declare const KNOWN_COMPONENT_TYPE_CANDIDATES: readonly string[]; // ── KanbanConfig (type) ── type KanbanConfig = z.input; +// ── KanbanConfigParsed (type) ── +type KanbanConfigParsed = z.infer; + // ── KanbanConfigSchema (const) ── declare const KanbanConfigSchema: z.ZodObject<{ groupByField: z.ZodString; summarizeField: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>; // ── LIST_VIEW_GROUP_COUNT_ALIAS (const) ── @@ -10015,6 +10023,7 @@ declare const ListViewSchema: z.ZodObject<{ summarizeField: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; timeline: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>; // ── TreeConfig (type) ── @@ -20575,6 +20590,7 @@ declare const VIEW_METADATA_MEMBERS: { }>>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; timeline: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional>; titleField: z.ZodOptional; visibleFields: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; chart: z.ZodOptional; titleField: z.ZodOptional; columns: z.ZodArray; + limit: z.ZodDefault; }, z.core.$strict>>; gantt: z.ZodOptional>; + limit: z.ZodDefault; }, z.core.$strict>>; calendar: z.ZodOptional Date: Sun, 20 Sep 2026 00:44:02 +0000 Subject: [PATCH 4/5] test(spec): assert the scope pin on the refused KEY LIST, not on a stringified issue Measured during the ablation that falsifies it: with a ceiling planted on the gantt config there is no `unrecognized_keys` issue to stringify, so the case reddened with an argument-type complaint instead of a sentence about gantt. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- packages/spec/src/ui/view.test.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/spec/src/ui/view.test.ts b/packages/spec/src/ui/view.test.ts index 0047dc4745a..191ec8e982b 100644 --- a/packages/spec/src/ui/view.test.ts +++ b/packages/spec/src/ui/view.test.ts @@ -4765,9 +4765,15 @@ describe('view row ceiling — `limit` on the page-shaped view configs (#17393)' for (const [label, schema] of NON_GRID_FOUR) { const result = schema.safeParse({ limit: 10 }); expect(result.success, label).toBe(false); - const unrecognized = ((result as { error?: z.ZodError }).error?.issues ?? []) - .find((issue) => issue.code === 'unrecognized_keys'); - expect(JSON.stringify(unrecognized), label).toContain('limit'); + // Asserted on the REFUSED KEY LIST rather than on a stringified issue: + // when the key is accepted there is no issue to stringify, and the red + // then reads as an argument-type complaint instead of as a statement + // about this view type. Measured — it is how this case first reddened. + const refused = ((result as { error?: z.ZodError }).error?.issues ?? []) + .filter((issue) => issue.code === 'unrecognized_keys') + .flatMap((issue) => (issue as unknown as { keys?: string[] }).keys ?? []); + expect(refused, `${label} accepts an authorable row ceiling it should not declare`) + .toContain('limit'); } }); }); From e1ae025756fe1b1d13c2947d9845414ef7ffafc8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 20 Sep 2026 02:43:38 +0000 Subject: [PATCH 5/5] chore(spec): regenerate the whole artifact chain on the merged tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sync with origin/main through scripts/pm/os-regen-merge.sh, whose step 2 restored main's side of the four os-regen artifacts both branches moved (api-surface/ui.json, api-surface-declarations/ui.txt, authorable-surface/ui.json, export-origins/ui.json) — the driver had merged them exit 0 while silently keeping one side. Regenerated from the merged tree after a real build, so both sides' entries are present: #19219's element-level `navigation` rows and its ObjectTimelineProps block, and this branch's three `limit` rows plus DEFAULT_VIEW_ROW_LIMIT and KanbanConfigParsed. gen:docs adds the timeline row to the component reference page. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- content/docs/references/ui/component.mdx | 1 + packages/spec/api-surface-declarations/ui.txt | 290 +++++++++++++++++- packages/spec/api-surface/ui.json | 3 + packages/spec/authorable-surface/ui.json | 17 + packages/spec/export-origins/ui.json | 3 + 5 files changed, 312 insertions(+), 2 deletions(-) diff --git a/content/docs/references/ui/component.mdx b/content/docs/references/ui/component.mdx index 4c6811e7a79..4c719151932 100644 --- a/content/docs/references/ui/component.mdx +++ b/content/docs/references/ui/component.mdx @@ -828,6 +828,7 @@ View filter rule | **groupByField** | `string` | optional | Field to group timeline rows. NO leading or trailing whitespace: the renderer reads this name off every row verbatim, so a padded spelling drops every row into one ungrouped band. | | **colorField** | `string` | optional | Field to derive each item color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the timeline default marker color | | **scale** | `Enum<'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>` | optional (default: `"week"`) | Default timeline scale | +| **limit** | `integer` | optional (default: `100`) | Row ceiling — the most rows the timeline fetches onto its rail, sent as the query `$top`; default 100 when the key is absent. When the ceiling APPLIES (the filtered set is larger than it), the renderer must show a visible truncation signal saying what is on screen is not the whole set — a bounded view that looks complete is worse than an unbounded one. | ### Nested Shape: `ObjectTimelineProps.filter[number]` diff --git a/packages/spec/api-surface-declarations/ui.txt b/packages/spec/api-surface-declarations/ui.txt index 38369600f77..ec6a2389bbe 100644 --- a/packages/spec/api-surface-declarations/ui.txt +++ b/packages/spec/api-surface-declarations/ui.txt @@ -12,8 +12,8 @@ # excluded: documentation drift is `check:docs`'s axis, not this one. # # entry: ./ui -# exported names: 473 -# declarations: 487 +# exported names: 476 +# declarations: 490 # # GENERATED — ⛔ never hand-edited. Regenerate after a real build: # pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec gen:api-surface-declarations @@ -4679,6 +4679,29 @@ declare const ComponentPropsMap: { quickAdd: z.ZodOptional; coverImageField: z.ZodOptional; conditionalFormatting: z.ZodOptional; + navigation: z.ZodOptional>; + view: z.ZodOptional; + preventNavigation: z.ZodDefault; + openNewTab: z.ZodDefault; + size: z.ZodDefault>; + width: z.ZodOptional>; + }, z.core.$strict>>; }, z.core.$strict>; readonly 'object-calendar': z.ZodObject<{ objectName: z.ZodOptional; @@ -4725,6 +4748,29 @@ declare const ComponentPropsMap: { staticData: z.ZodOptional>; locale: z.ZodOptional; loading: z.ZodOptional; + navigation: z.ZodOptional>; + view: z.ZodOptional; + preventNavigation: z.ZodDefault; + openNewTab: z.ZodDefault; + size: z.ZodDefault>; + width: z.ZodOptional>; + }, z.core.$strict>>; }, z.core.$strict>; readonly 'object-form': z.ZodObject<{ objectName: z.ZodOptional; @@ -5273,6 +5319,99 @@ declare const ComponentPropsMap: { }, z.core.$strict>>; navigation: z.ZodOptional; }, z.core.$strict>; + readonly 'object-timeline': z.ZodObject<{ + objectName: z.ZodOptional; + timeline: z.ZodOptional; + titleField: z.ZodString; + groupByField: z.ZodOptional; + colorField: z.ZodOptional; + scale: z.ZodDefault>; + limit: z.ZodDefault; + }, z.core.$strict>>; + filter: z.ZodOptional>; + value: z.ZodOptional>]>>; + }, z.core.$strict>>>; + sort: z.ZodOptional; + }, z.core.$strip>>>; + limit: z.ZodOptional; + data: z.ZodOptional>; + items: z.ZodOptional>; + variant: z.ZodOptional>; + dateFormat: z.ZodOptional>; + rowLabel: z.ZodOptional; + minDate: z.ZodOptional; + maxDate: z.ZodOptional; + descriptionField: z.ZodOptional; + mapping: z.ZodOptional; + navigation: z.ZodOptional>; + view: z.ZodOptional; + preventNavigation: z.ZodDefault; + openNewTab: z.ZodDefault; + size: z.ZodDefault>; + width: z.ZodOptional>; + }, z.core.$strict>>; + }, z.core.$strict>; }; // ── DATE_RANGE_DEFAULT_RANGES (const) ── @@ -10703,6 +10842,29 @@ declare const ObjectCalendarPropsSchema: z.ZodObject<{ staticData: z.ZodOptional>; locale: z.ZodOptional; loading: z.ZodOptional; + navigation: z.ZodOptional>; + view: z.ZodOptional; + preventNavigation: z.ZodDefault; + openNewTab: z.ZodDefault; + size: z.ZodDefault>; + width: z.ZodOptional>; + }, z.core.$strict>>; }, z.core.$strict>; // ── ObjectFormProps (type) ── @@ -11232,6 +11394,29 @@ declare const ObjectKanbanPropsSchema: z.ZodObject<{ quickAdd: z.ZodOptional; coverImageField: z.ZodOptional; conditionalFormatting: z.ZodOptional; + navigation: z.ZodOptional>; + view: z.ZodOptional; + preventNavigation: z.ZodDefault; + openNewTab: z.ZodDefault; + size: z.ZodDefault>; + width: z.ZodOptional>; + }, z.core.$strict>>; }, z.core.$strict>; // ── ObjectListViewSchema (const) ── @@ -12299,6 +12484,107 @@ declare const ObjectNavItemSchema: z.ZodObject<{ requiresService: z.ZodOptional; }, z.core.$strict>; +// ── ObjectTimelineProps (type) ── +type ObjectTimelineProps = z.input; + +// ── ObjectTimelinePropsParsed (type) ── +type ObjectTimelinePropsParsed = z.infer; + +// ── ObjectTimelinePropsSchema (const) ── +declare const ObjectTimelinePropsSchema: z.ZodObject<{ + objectName: z.ZodOptional; + timeline: z.ZodOptional; + titleField: z.ZodString; + groupByField: z.ZodOptional; + colorField: z.ZodOptional; + scale: z.ZodDefault>; + limit: z.ZodDefault; + }, z.core.$strict>>; + filter: z.ZodOptional>; + value: z.ZodOptional>]>>; + }, z.core.$strict>>>; + sort: z.ZodOptional; + }, z.core.$strip>>>; + limit: z.ZodOptional; + data: z.ZodOptional>; + items: z.ZodOptional>; + variant: z.ZodOptional>; + dateFormat: z.ZodOptional>; + rowLabel: z.ZodOptional; + minDate: z.ZodOptional; + maxDate: z.ZodOptional; + descriptionField: z.ZodOptional; + mapping: z.ZodOptional; + navigation: z.ZodOptional>; + view: z.ZodOptional; + preventNavigation: z.ZodDefault; + openNewTab: z.ZodDefault; + size: z.ZodDefault>; + width: z.ZodOptional>; + }, z.core.$strict>>; +}, z.core.$strict>; + // ── ObjectTreeProps (type) ── type ObjectTreeProps = z.input; diff --git a/packages/spec/api-surface/ui.json b/packages/spec/api-surface/ui.json index 89c080a12a5..720e2c2f464 100644 --- a/packages/spec/api-surface/ui.json +++ b/packages/spec/api-surface/ui.json @@ -279,6 +279,9 @@ "ObjectNavItem (type)", "ObjectNavItemParsed (type)", "ObjectNavItemSchema (const)", + "ObjectTimelineProps (type)", + "ObjectTimelinePropsParsed (type)", + "ObjectTimelinePropsSchema (const)", "ObjectTreeProps (type)", "ObjectTreePropsParsed (type)", "ObjectTreePropsSchema (const)", diff --git a/packages/spec/authorable-surface/ui.json b/packages/spec/authorable-surface/ui.json index 0510caa7720..32cf71ec886 100644 --- a/packages/spec/authorable-surface/ui.json +++ b/packages/spec/authorable-surface/ui.json @@ -696,6 +696,7 @@ "ui/ObjectCalendarProps:filter", "ui/ObjectCalendarProps:loading", "ui/ObjectCalendarProps:locale", + "ui/ObjectCalendarProps:navigation", "ui/ObjectCalendarProps:objectName", "ui/ObjectCalendarProps:sort", "ui/ObjectCalendarProps:staticData", @@ -802,6 +803,7 @@ "ui/ObjectKanbanProps:groupBy", "ui/ObjectKanbanProps:grouping", "ui/ObjectKanbanProps:limit", + "ui/ObjectKanbanProps:navigation", "ui/ObjectKanbanProps:objectName", "ui/ObjectKanbanProps:quickAdd [RETIRED]", "ui/ObjectKanbanProps:swimlaneField", @@ -914,6 +916,21 @@ "ui/ObjectNavItem:type", "ui/ObjectNavItem:viewName", "ui/ObjectNavItem:visible", + "ui/ObjectTimelineProps:data", + "ui/ObjectTimelineProps:dateFormat", + "ui/ObjectTimelineProps:descriptionField", + "ui/ObjectTimelineProps:filter", + "ui/ObjectTimelineProps:items", + "ui/ObjectTimelineProps:limit", + "ui/ObjectTimelineProps:mapping", + "ui/ObjectTimelineProps:maxDate", + "ui/ObjectTimelineProps:minDate", + "ui/ObjectTimelineProps:navigation", + "ui/ObjectTimelineProps:objectName", + "ui/ObjectTimelineProps:rowLabel", + "ui/ObjectTimelineProps:sort", + "ui/ObjectTimelineProps:timeline", + "ui/ObjectTimelineProps:variant", "ui/ObjectTreeProps:data", "ui/ObjectTreeProps:filter", "ui/ObjectTreeProps:navigation", diff --git a/packages/spec/export-origins/ui.json b/packages/spec/export-origins/ui.json index a5ebd5d66b7..d19dff2033f 100644 --- a/packages/spec/export-origins/ui.json +++ b/packages/spec/export-origins/ui.json @@ -275,6 +275,9 @@ "ObjectNavItem": "src/ui/app.zod.ts#ObjectNavItem (type)", "ObjectNavItemParsed": "src/ui/app.zod.ts#ObjectNavItemParsed (type)", "ObjectNavItemSchema": "src/ui/app.zod.ts#ObjectNavItemSchema (const)", + "ObjectTimelineProps": "src/ui/component.zod.ts#ObjectTimelineProps (type)", + "ObjectTimelinePropsParsed": "src/ui/component.zod.ts#ObjectTimelinePropsParsed (type)", + "ObjectTimelinePropsSchema": "src/ui/component.zod.ts#ObjectTimelinePropsSchema (const)", "ObjectTreeProps": "src/ui/component.zod.ts#ObjectTreeProps (type)", "ObjectTreePropsParsed": "src/ui/component.zod.ts#ObjectTreePropsParsed (type)", "ObjectTreePropsSchema": "src/ui/component.zod.ts#ObjectTreePropsSchema (const)",