Skip to content

feat(app-shell,plugin-kanban): a card on a kanban view leaf's Studio canvas selects the board's card in a 看板卡片 section (objectui#12725, part) - #12900

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-12725-kanban-card-panel
Oct 17, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-12725-kanban-card-panel

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #12725
Clause-②: no
This lands the release's remainder item 1: a kanban view leaf's card. Still on #12725: item 3's 草稿 chip and 来源 line, which need the copilot's draft provenance per field, and items 4 and 5 (change card v2, the 变更 badge counting the copilot's drafts), which need objectstack-ai/cloud#2924's payload.

What changed

On an object leaf whose list view renders as a kanban board (type: 'kanban'), a click on a card in the Studio canvas selects "the card":

  • the right slot switches to 属性 and shows the new 看板卡片 section, marked selected; the copilot folds to its strip at the slot's foot;
  • the clicked card is outlined;
  • the status line reads 「已选中:看板卡片 · Esc 取消选中」.

The click is taken in the capture phase, so the record does not open. The pointer press on a card is taken there too, so the board's drag sensor never starts a move on the Studio canvas. Enter, and Space on its release, on a focused card select it the same way (see objectui#12888 below). A button or link drawn inside a card keeps its click and its keys. Esc, or a click on empty canvas, clears the selection, and the slot keeps the view's properties. Outside Studio the board is unchanged: a click opens the record and a drag moves the card.

  • studio-canvas-preview.tsx: a Studio-internal StudioCanvasCardSelectionContext beside the published props, in the pattern of phase 3's StudioCanvasColumnSelectionContext. The default object canvas wraps the grid in one more display: contents element (KanbanCardSelect) that reads it. StudioCanvasPreviewProps and index.ts are unchanged, and a canvas registered over the default sees no channel.
  • StudioDesignSurface.tsx: the card is a member of the pillar's one keyed selection, { kind: 'card', id: RECORD_ID }, beside phase 3's { kind: 'column' }, not a second, parallel state. So showProps, Esc and the empty-canvas click are the existing seam. The channel is provided only while the panel holds a kanban view of the open leaf.
  • ObjectListViewInspector.tsx: the 看板卡片 section, shown only when the view's type is kanban, first in the panel, writing through the inspector's existing draft path.
  • metadata-admin/i18n.ts: 17 en and zh rows. No @object-ui/i18n key.

How a card is identified, and plugin-kanban

SortableCard gains ONE additive attribute, data-kanban-card set to the card's record id; nothing else in plugin-kanban changes, so objectui#12838's aria-label work in the same file stays clear. objectui#12888 (merged into this branch) split the card into a role="listitem" wrapping a role="button" control that holds the ref, the drag listeners, the click and the new Enter and Space handlers; the attribute sits on that control, so closest('[data-kanban-card]') and the outline rule each resolve to exactly one element per card. Because #12888 opens a focused card on Enter and on Space's release, the canvas takes those keys in the capture phase too, only on the card element itself (#12888's own guard): Enter and Space's release select the card, Space's press is held so the board does not scroll, and neither reaches the card's handlers. Outside Studio the keys open the record, as #12888 made them. Measured why the existing DOM was not enough: role="listitem" alone does find a card (the board draws no other list item), but the outline has to name the clicked card, and aria-label is the card's title, which is record text that two cards can share (and a translated or derived title besides). A style rule keyed on it would outline every card of that title. The record id names exactly one card.

The outline is a style rule scoped to the wrapper, as phase 3's column outline is: the board owns its cards and redraws them, so no class can be put on a card from outside.

The four controls

Each writes one key the spec declares, and only that key (@objectstack/spec 18.0.0-next.3, unchanged from next.2 for these keys):

control key written spec line it rests on
卡片字段 (card fields), ordered kanban.columns KanbanConfigSchema.columns: "Fields to show on cards"
标题 (title) kanban.titleField; 记录名称 removes the key KanbanConfigSchema.titleField: "Omit to fall back to the record display name (ADR-0079 resolver chain)"
点击打开 (click to open) navigation.mode; 默认 removes the key (and an empty navigation block with it) NavigationConfigSchema.mode over NavigationModeSchema: page, drawer, modal, split, popover, new_window, none
分组字段 (group by) kanban.groupByField KanbanConfigSchema.groupByField "Field to group columns by", checked by groupByFieldCheck, which refuses only a padded name; every field of the object is offered

KanbanConfigSchema is a strict object that requires groupByField and columns. So on a kanban view with no kanban block, the title and the card fields are disabled with a note until a group-by field is picked, and picking it writes { groupByField, columns: [] }; on an empty kanban.columns the board shows the view's first columns, as before. No colour control (rowColor has no reader in plugin-kanban), and no summarizeField control (the StudioRailProps artboard draws none); an existing summarizeField is kept as written.

Live, before and after

e2e/live/ci/start-backend.sh (published @objectstack/*@18.0.0-next.3, showcase 2c9bf9430a11), console from main 26323c4 (before) and this branch 9591cd2 (after), Chromium, light, zh-CN, 1280 and 1010 px. Leaf: 界面 › 数据模型 › 任务看板(看板视图), the showcase's own board kanban view of showcase_task.

  • Before: a card click opens the record-detail overlay (「Showcase task详情」), the slot stays on the copilot, nothing is selected. The status line already says 「点元素 → 右栏切到属性」 there, and nothing answers it.
  • After: the slot switches to 属性 with 看板卡片 selected, the card is outlined, the status line names it, no overlay opens and the URL does not change. Esc and an empty-canvas click clear it; the slot stays on 属性.

Deviations

  • The showcase declares kanban list views (task.view.ts board, project.view.ts by_status), but no object nav entry of the showcase lands on one, so there is no kanban view leaf to open (phase 3 measured the same). The live reading adds one entry in the browser, { type: 'object', objectName: 'showcase_task', viewName: 'board' }, identically for before and after. The README beside the screenshots lists every stub.
  • plugin-kanban is touched by the one attribute above, as ruling 3 allows when the existing DOM cannot identify a card.

Acceptance notes

  • Keyboard activation selects a card too: since objectui#12888 a focused card opens on Enter and Space, so on the Studio canvas those keys select it, as a click does.
  • While a card keeps keyboard focus, the browser's focus ring on its control draws over the selection outline (after-1280-card-enter.png).
  • At 1010 px the item header's title is drawn with no visible text beside its chips, on main and on this branch alike (before-1010-open.png).

Screenshots

Evidence branch claude/issue-12725d-evidence, commits 1f37ce5 (the click reading) and d2eac2e (the Enter reading), under docs/design/findings/12725d/ (18 PNGs, the DOM readings as JSON, the driver and a README):

Test evidence (head 9bce2a9; the suites ran on 14078da, and 9bce2a9 changes only the changeset's prose)

  • New: StudioDesignSurface.kanbanCardSelection-12725.test.tsx, 16 tests through the mounted surface with the real StudioObjectRecordsCanvas and the real kanban board: the card click (slot 属性, section selected, outline, status line, no record opened, the press never reaches the card); Enter on a focused card selects it; Space selects on its release, and neither half reaches the card's own handlers; a control inside a card keeps its click and its keys; Esc; empty canvas; each control writes exactly its key and the saved body parses as ListViewSchema; a view with no kanban block. Controls: a grid leaf has no section; the board outside Studio opens the record on a click and on Enter, and takes the press; a canvas registered over the default gets no channel.
  • Every StudioDesignSurface*, studioCanvas* / studio-canvas-preview and ObjectListViewInspector* test: 97 files, 671 tests passed. packages/plugin-kanban/ (objectui#12888's tests included): 75 files, 506 tests passed. Phase 3's leafColumnSelection-12725 is in the family run, unchanged and green.
  • type-check for @object-ui/app-shell and @object-ui/plugin-kanban: exit 0. build for both: exit 0. eslint (normal mode) on the changed files: 0 errors, and the diff adds no eslint-disable directive; the new warnings are react-refresh/only-export-components on the new exports, the rule already firing in both files.
  • check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:new-line-citations (0 new), check:test-path-roots, check-changeset-presence: all exit 0.
  • Ablation, one mutant per new pin, through objectstack's ablation-replace.mjs in wrap mode (each restore proven with an empty git diff HEAD): the card select off, the click's stopPropagation off, the press capture off, the outline naming another card, the control exemption off, data-kanban-card removed, the channel not provided, Esc off, empty-canvas clear off, showProps off, the section on every view, the section not marked selected, each of the four writes broken, the record-name option keeping the title, a born block without columns, the title not held for the block; after objectui#12888, Enter not taken, Space's release not taken, Space's press not held, the own-key guard widened to anything inside a card, the key path not stopped, and the attribute removed again: each turned at least one test red. Not pinned, by measurement: the channel's kanban gate (no cards exist on a non-kanban canvas to tell it apart).

Generated by Claude Code

…s selects the board's card in the view's properties (objectui#12725, part)

On an object leaf whose list view renders as a kanban board, a card click
on the Studio canvas selects "the card": the pillar's one selection gains a
{ kind: 'card' } member, so the slot switches to 属性, Esc and an empty
canvas click clear it, and the status line names it. The click and the
pointer press are taken in the capture phase, so the record does not open
and no drag starts; a control inside a card keeps its job.

The channel is a Studio-internal StudioCanvasCardSelectionContext beside the
published props; StudioCanvasPreviewProps and index.ts are unchanged.

ObjectListViewInspector gains a 看板卡片 section on a kanban view. Each
control writes one spec-declared key: kanban.columns, kanban.titleField,
navigation.mode and kanban.groupByField. No colour control.

SortableCard carries one additive data-kanban-card attribute with the
card's record id, so the canvas can tell which card a click landed on and
outline it.

Claude-Session: https://claude.ai/code/session_01HRnFN6Rtqk2WxPiK6qBhwq
Co-authored-by: Claude <noreply@anthropic.com>
…ction (objectui#12725, part)

Through the mounted surface with the real board: a card click switches the
slot to 属性, outlines the card and names it on the status line, and neither
opens the record nor lets the press reach the card; Esc and an empty-canvas
click clear it. Each of the four controls writes exactly its spec key and the
saved body parses as ListViewSchema; a view with no kanban block is born by
the group-by field. Controls: a grid leaf has no section, the board outside
Studio opens a record and takes the press, a canvas registered over the
default gets no channel.

Claude-Session: https://claude.ai/code/session_01HRnFN6Rtqk2WxPiK6qBhwq
Co-authored-by: Claude <noreply@anthropic.com>
… on the Studio canvas (objectui#12725, part)

Claude-Session: https://claude.ai/code/session_01HRnFN6Rtqk2WxPiK6qBhwq
Co-authored-by: Claude <noreply@anthropic.com>
objectui#12888 split SortableCard into a list item wrapping a role=button
control; data-kanban-card moves to the control, which holds the ref, the
listeners and the click and key handlers.

Claude-Session: https://claude.ai/code/session_01HRnFN6Rtqk2WxPiK6qBhwq
Co-authored-by: Claude <noreply@anthropic.com>
…n the Studio canvas (objectui#12725, part)

objectui#12888 made a focused card open on Enter and on Space's release, as
a click does. On the Studio canvas the keys now do what the click does there:
KanbanCardSelect takes keydown Enter and keyup Space in the capture phase on
the card element itself and selects the card, and holds Space's press so the
board does not scroll and the board's own handler does not run. A key on a
control inside a card stays that control's. Outside Studio nothing changes.

Pins: Enter selects, Space selects on release, a key on a control inside a
card is not taken, and the CONTROL that Enter opens the record outside Studio.
The fixture card follows the board's new list item and button nesting.

Claude-Session: https://claude.ai/code/session_01HRnFN6Rtqk2WxPiK6qBhwq
Co-authored-by: Claude <noreply@anthropic.com>
…ndlers on the Studio canvas (objectui#12725, part)

Claude-Session: https://claude.ai/code/session_01HRnFN6Rtqk2WxPiK6qBhwq
Co-authored-by: Claude <noreply@anthropic.com>
…(objectui#12725, part)

Claude-Session: https://claude.ai/code/session_01HRnFN6Rtqk2WxPiK6qBhwq
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 75 chunks) 3153.0 KB 3744.1 KB
Main entry chunk (gzip) 80.4 KB 350 KB
Entry file index-DQrgsuMM.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 21.42KB 7.85KB
app-shell (runtime-config.js) 23.60KB 8.19KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 45.59KB 12.03KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 20.67KB 6.12KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.74KB 3.94KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.62KB 0.83KB
auth (authStyles.js) 5.27KB 1.86KB
auth (createAuthClient.js) 42.84KB 11.66KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 608.83KB 147.74KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 248.83KB 69.44KB
fields (index.js) 291.60KB 74.45KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.33KB 11.96KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 54.94KB 16.05KB
plugin-charts (index.js) 84.97KB 23.36KB
plugin-chatbot (index.js) 210.60KB 49.94KB
plugin-dashboard (index.js) 146.23KB 39.59KB
plugin-designer (index.js) 233.95KB 50.08KB
plugin-detail (index.js) 265.18KB 70.24KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 180.22KB 46.59KB
plugin-gantt (index.js) 178.36KB 45.08KB
plugin-grid (index.js) 272.78KB 75.66KB
plugin-kanban (index.js) 62.93KB 19.40KB
plugin-list (index.js) 139.20KB 35.82KB
plugin-map (index.js) 27.07KB 8.96KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 46.66KB 13.13KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.27KB 5.40KB
plugin-view (index.js) 94.68KB 24.36KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.23KB 39.46KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · objectui#12725 phase 4 · head 9bce2a9 · 2026-10-17T15:26Z

Seat domain:ui#4, session session_01SJn1GEkrbbBQkhGC26wEDw. This record was checked against GitHub and the diff, not against the report, which is relayed on the card. The seat opened this PR from the dev's drafts (degraded channel), and the body and title are those drafts verbatim.

Checklist

  • Form:
    • Draft → main, assignee os-sales.
    • The first line is Part of #12725, and the second is Clause-②: no, as the claim declares (6234132988). The body names what stays on the card: item 3's 草稿 chip and 来源 line, and items 4 and 5.
    • There are six change commits and two plain merges of origin/main (9591cd2 for 26323c4, 02bd584 for 1d872f2). Each carries only the model-free trailer pair, and the trailers are the last lines of every message.
    • git merge-tree against current main (8e75978) is clean. Against main the diff is 7 files, +1016 / −7, all inside the claim's surface. There is no PNG, and it is not a governed surface.
  • The first review's blocker, resolved as ruled:
    • The merge with objectui#12888. That PR split SortableCard into a role="listitem" wrapper around a role="button" control. data-kanban-card sits on the inner control: the one that holds the ref, the listeners, the click and fix(plugin-kanban): a focused card opens on Enter and Space, as a click does #12888's key handlers. So closest('[data-kanban-card]') and the outline rule ([data-kanban-card=…]>*) each find exactly one element per card. KanbanImpl.tsx's whole diff against main is that attribute and its two comment lines.
    • The keys. KanbanCardSelect takes them in the capture phase, but only when the key's target is the card element itself (matches('[data-kanban-card]')), the same guard fix(plugin-kanban): a focused card opens on Enter and Space, as a click does #12888 uses. Enter on keydown and Space on keyup select the card; Space on keydown is held. All three call preventDefault and stopPropagation, so neither fix(plugin-kanban): a focused card opens on Enter and Space, as a click does #12888's handler nor the board's scroll sees them. A key on a control inside a card is left alone. Outside Studio nothing changes.
    • Pins (a)–(d) are in the test file, now 16 tests.
      • The first ablation of Space's press (K3) stayed green, because the board's own handler also calls preventDefault. The dev strengthened pin (b) with native listeners on the card, and K3 then turned red. That is the right fix: the pin now proves the press never reaches the card, not only that the page did not scroll.
      • K1, K2, K4 and K5 each turned red. D1, re-run on the new nesting, turned 13 tests red.
  • The diff, read by the seat:
    • No lint directive is added.
    • The 17 new rows are under engine.studio.inspector.listView.kanbanCard.* in metadata-admin/i18n.ts. That is not an ADR-0060 frozen namespace, and no key leads with a product word.
    • StudioCanvasPreviewProps and index.ts are unchanged.
  • Screenshots, read by the seat (never-merged claude/issue-12725d-evidence, 1f37ce5 and d2eac2e; zh, light):
    • On main at 1280 px, a card click opens 「Showcase task详情」.
    • On the branch, the click selects the card: 属性 with 看板卡片 framed, the card outlined, and 「已选中:看板卡片 · Esc 取消选中」.
    • On 9bce2a9, Enter on a focused card gives the same selection at 1280 and 1010 px, with the focus ring drawn over the outline, as the Acceptance notes say.
    • Sent to the maintainer at 2026-10-17T15:26Z.
  • Tests:
  • Changeset: one file: minor for @object-ui/app-shell, patch for @object-ui/plugin-kanban. Its prose now names Enter and Space.
  • CI: running at this record. Landing waits for green.

Rulings

  1. The five open questions of the first report: A, as built. These were the channel, the group-by picker offering every field, a kanban block born on the first group-by pick, one keyed selection with its own narrow channel, and 默认 for an absent navigation.mode.
  2. The worktree deviation is noted, not a breach. The classifier refused git branch -D. The dev did not delete the branch some other way: they attached the existing branch, which had no commits of its own, to a new worktree. The shared checkout is left detached and clean.
  3. The correction is accepted. The first report's note that a keyboard drag is not intercepted was wrong, because the board builds only pointer and touch sensors, and it is dropped from the body.
  4. The class-a finding is carried by objectui#12897, filed by this seat for triage: the Interfaces item header's title is empty at 1010 px, on main too.
  5. On merge, the card stays open (Part of). It goes back to pm:queue with items 3–5 named, its assignee cleared, and pm:dispatched removed.

Implemented-by: cloud session session_01HRnFN6Rtqk2WxPiK6qBhwq, branch claude/issue-12725-kanban-card-panel


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Landing · head 9bce2a9 · 2026-10-17T15:38Z

  • Head since the ACCEPT: unchanged at 9bce2a9. The ACCEPT (6235740075) was checked against this exact commit, so its rulings stand.
  • Checks on 9bce2a9: 36 runs: 33 green, 3 expected skips, none red. The green runs include:
    • Build & E2E and Live E2E (informational);
    • Repo Gates, Type Check and Lint;
    • all 16 test shards, which include the full app-shell suite the dev declared to CI and the ADR-0060 ratchet;
    • Bundle Analysis (eager closure 3153.0 of 3744.1 KB);
    • Governed Surface Queue Guard.
  • Merge state: clean against main 8e75978. GitHub reports mergeable_state: clean, and git merge-tree agrees.
  • Commit messages: all eight commits (six changes and two plain merges of origin/main) carry only the model-free trailer pair, and the trailers are last.
  • Action: mark the PR ready for review and enable auto-merge through the merge queue. This is Part of #12725: on merge the card stays open, and the seat releases it to pm:queue with items 3–5 named.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 17, 2026 15:39
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 17, 2026
Merged via the queue into main with commit f5009db Oct 17, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12725-kanban-card-panel branch October 17, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants