Page picker: one TreeList for both pickers, tree or list view by module and param, the page field's value row #348

Closed
opened 2026-10-24 10:36:19 +02:00 by dominik.polakovics · 0 comments

Mockup (definition agreed 2026-10-24): https://claude.ai/artifact/BN7XprNTpmx1ZjigEcVCET — "Proposal" (pins 1–8), "The field that opens it", "Decisions" A–G, "What changes where". The list-view variant for forms: https://claude.ai/artifact/XC9KeADJfRrjD428fZFMdj §5.

Depends on #347 (tree: hidden in GET /api/modules; the shared item row from #346). Third of three.

What changes

One tree component

TreeBrowser.tsx is retired; PagePicker.tsx uses TreeList.tsx, which gains: an address column (permalink, mono, muted, right-aligned on every row; folds under the title at phone width), a muted eligibility style (an ineligible row keeps readable --color-text-muted text at 500 weight, plain cursor, its caret live, a tooltip with the reason) replacing .tree-row__label--disabled's opacity, icons (house, folder, page, eye-off for hidden) and the existing disabled treatment (struck + .badge--warn). findChain moves with it. The link browser's Page tab gets the address column and muted rows in the same change.

The page picker dialog (PagePicker.tsx)

  • The lg dialog (960px, fixed height), the tree filling its full width. Head: "Choose a page" (section role) + a sub line naming the constraint by the page type's label ("Only pages of the type Service can be chosen."). Close button.
  • Toolbar: the search field (filter: matches and ancestors, all open, as the link browser does), a .toggle Tree / List, and a .choice checkbox "Only pages" that folds the tree to eligible pages and their ancestors (per-viewer, localStorage, try/catch).
  • Tree or list is decided by the module, overridable by the param: a pageType owned by a module whose tree is hidden (GET /api/modules) opens on the list with no Tree option; otherwise the tree with the toggle. New param setting picker: tree | list pins one view and hides the toggle. The list view: the eligible pages, by title, as .list rows with radio choice, address meta and status badge; when a module owns the type it is GET /api/module/entries through modulequery (the module screen's own query, one owner), else the tree flattened.
  • Eligibility stays PagePicker's one evaluation over pagemanage.effectiveNodeType; the row's reason text derives from that verdict. Disabled pages are refused (as link.disabledTitle), listed struck with the badge and a hint. Hidden pages wear eye-off and a neutral "Hidden" badge. The stored value is pre-selected and wears a "Current" chip.
  • Foot: "Chosen:
Mockup (definition agreed 2026-10-24): https://claude.ai/artifact/BN7XprNTpmx1ZjigEcVCET — "Proposal" (pins 1–8), "The field that opens it", "Decisions" A–G, "What changes where". The list-view variant for forms: https://claude.ai/artifact/XC9KeADJfRrjD428fZFMdj §5. Depends on #347 (`tree: hidden` in GET /api/modules; the shared item row from #346). Third of three. ## What changes ### One tree component `TreeBrowser.tsx` is retired; `PagePicker.tsx` uses `TreeList.tsx`, which gains: an **address column** (`permalink`, mono, muted, right-aligned on every row; folds under the title at phone width), a **muted eligibility style** (an ineligible row keeps readable `--color-text-muted` text at 500 weight, plain cursor, its caret live, a tooltip with the reason) replacing `.tree-row__label--disabled`'s opacity, icons (house, folder, page, eye-off for hidden) and the existing disabled treatment (struck + `.badge--warn`). `findChain` moves with it. The link browser's Page tab gets the address column and muted rows in the same change. ### The page picker dialog (`PagePicker.tsx`) - The lg dialog (960px, fixed height), the tree filling its full width. Head: "Choose a page" (section role) + a sub line naming the constraint by the page type's label ("Only pages of the type Service can be chosen."). Close button. - Toolbar: the search field (filter: matches and ancestors, all open, as the link browser does), a `.toggle` **Tree / List**, and a `.choice` checkbox **"Only <type> pages"** that folds the tree to eligible pages and their ancestors (per-viewer, `localStorage`, try/catch). - **Tree or list is decided by the module, overridable by the param**: a `pageType` owned by a module whose `tree` is `hidden` (GET /api/modules) opens on the list with no Tree option; otherwise the tree with the toggle. New param setting **`picker: tree | list`** pins one view and hides the toggle. The list view: the eligible pages, by title, as `.list` rows with radio choice, address meta and status badge; when a module owns the type it is GET /api/module/entries through `modulequery` (the module screen's own query, one owner), else the tree flattened. - Eligibility stays `PagePicker`'s one evaluation over `pagemanage.effectiveNodeType`; the row's reason text derives from that verdict. **Disabled pages are refused** (as `link.disabledTitle`), listed struck with the badge and a hint. Hidden pages wear eye-off and a neutral "Hidden" badge. The stored value is pre-selected and wears a "Current" chip. - Foot: "Chosen: <title> <address>" at the left, ghost Cancel, primary Choose (disabled while nothing eligible is selected). - Translations: the picker browses the selected language's tree; an untranslated page keeps its default-language title in italics with the code tag, choosable (the link falls back). ### The `page` field (`widgets/controls.tsx` PagePickerControl) The image-field chrome is replaced by a **value row**: icon, title, the resolved address (mono), the page's git badge where `useResolvedLink` carries it, and small actions Change… (bordered) / Clear (ghost); empty state a dashed row "No page chosen" + "Choose a page…"; the shared `.field`, `.label`, `.hint`; an `.error` line when the stored page is disabled ("This page is disabled and will not be built. Choose another or enable it."). The image and link fields keep their chrome this round (follow-up issue). ### Documents, skills, owners (CLAUDE.md inventory) - Documents: `scaffold/site/AGENTS.md` field types — the `page` type: `pageType` filtering, tree vs list, the new `picker` setting (named there so `internal/element/agentsdoc_test.go` passes); `docs/styleguide/styleguide.html` if the picker tree row becomes a shared sample (else product-only in `styles.css`); `docs/api.md` unchanged (no route change). - Skills: `add-element` (a `page` param, its `pageType` and `picker`), `edit-content` if it names the picker, `create-feature` if it mentions the picker a module's type gets. - Rule owners: eligibility `PagePicker`/`pagemanage.effectiveNodeType`; disabled the tree's `enabled`; hidden the server's `hidden`; "which module owns this type" read from GET /api/modules (the server already enforces one module per type); list rows GET /api/module/entries via `modulequery`. No second evaluation anywhere. - i18n: `pagePicker.*`, new strings for the sub line, the checkbox, the chosen summary, the field's error; EN and DE. ## Acceptance - A Form element's `form` param opens "Choose a form" as a list (no Tree option) on a site whose forms module hides its tree; a Service teaser's `page` param opens the tree with the toggle; `picker: list` on a param pins the list. - No opacity on any picker row; every ineligible row has a reason tooltip; every row shows its address; Choose is disabled until an eligible row is selected; a disabled page cannot be chosen and a field holding one shows the error line. - `grep -rn TreeBrowser web/src` finds nothing; link-browser Page-tab tests pass with the address column. - Drift tests: `internal/element/agentsdoc_test.go` (the `picker` setting is named), `web/` styles test, `scaffold/skills_test.go`, `docs/docs_test.go`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle#348
No description provided.