@godxjp/ui 31.26.2 → 31.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/agent/START-HERE.md +7 -7
  2. package/agent/components/BlockEditor.json +91 -0
  3. package/agent/components/CommandPalette.json +5 -0
  4. package/agent/components/EmojiPicker.json +78 -0
  5. package/agent/components/Markdown.json +16 -0
  6. package/agent/components/PageContainer.json +11 -1
  7. package/agent/components/PageCover.json +75 -0
  8. package/agent/components/Tree.json +6 -0
  9. package/agent/components-index.json +15 -0
  10. package/agent/components.json +282 -1
  11. package/agent/index.json +8 -8
  12. package/agent/llms.txt +7 -7
  13. package/agent/tokens.json +108 -0
  14. package/dist/components/data-display/tree.js +5 -0
  15. package/dist/components/data-entry/command-palette.d.ts +5 -0
  16. package/dist/components/data-entry/command-palette.js +1 -0
  17. package/dist/components/data-entry/emoji-data.d.ts +43 -0
  18. package/dist/components/data-entry/emoji-data.js +54 -0
  19. package/dist/components/data-entry/emoji-picker.d.ts +13 -0
  20. package/dist/components/data-entry/emoji-picker.js +272 -0
  21. package/dist/components/data-entry/index.d.ts +2 -0
  22. package/dist/components/data-entry/index.js +2 -0
  23. package/dist/components/layout/index.d.ts +2 -0
  24. package/dist/components/layout/index.js +2 -0
  25. package/dist/components/layout/page-container.d.ts +1 -1
  26. package/dist/components/layout/page-container.js +8 -1
  27. package/dist/components/layout/page-cover.d.ts +14 -0
  28. package/dist/components/layout/page-cover.js +127 -0
  29. package/dist/contracts/measurement.json +1 -1
  30. package/dist/i18n/messages/en.json +25 -0
  31. package/dist/i18n/messages/ja.json +25 -0
  32. package/dist/i18n/messages/vi.json +25 -0
  33. package/dist/props/components/data-display.prop.d.ts +6 -0
  34. package/dist/props/components/data-entry.prop.d.ts +26 -0
  35. package/dist/props/components/layout.prop.d.ts +49 -2
  36. package/dist/props/registry.d.ts +85 -1
  37. package/dist/props/registry.js +95 -1
  38. package/dist/styles/control.css +81 -0
  39. package/dist/styles/data-display-layout.css +315 -2
  40. package/dist/styles/focus-ring.css +4 -0
  41. package/dist/styles/layers.json +26 -1
  42. package/dist/styles/layout.css +113 -0
  43. package/dist/tokens/components/control.css +10 -0
  44. package/dist/tokens/components/data-display.css +9 -0
  45. package/dist/tokens/semantic/layout.css +14 -0
  46. package/docs/FRAME-COVERAGE-REPORT.md +5 -2
  47. package/docs/data-entry/block-editor.tsx +105 -0
  48. package/docs/data-entry/emoji-picker.tsx +35 -0
  49. package/docs/i18n/messages/en.json +40 -0
  50. package/docs/i18n/messages/ja.json +40 -0
  51. package/docs/i18n/messages/vi.json +40 -0
  52. package/docs/layout/page-cover.tsx +56 -0
  53. package/package.json +28 -4
  54. package/scripts/prune-css.mjs +1 -1
@@ -705,7 +705,7 @@
705
705
  "defaultValue": "\"document\"",
706
706
  "description": "What the page's top row IS, which decides the `<h1>`'s type step. \"document\" (default) = the row is the page's TITLE (a record, a form, a collection, a report): --page-title-font-size (h1, 20px) with the existing responsive step down at 720px; no attribute is emitted, so an existing page is byte-identical. \"chrome\" = the row is the surface's own furniture — a chat channel name, a mail subject line, an IDE tab — naming the thing the user is already INSIDE instead of announcing a document; the h1 takes --page-title-font-size-chrome (--heading-h3 = the 14px body step) at EVERY width, including below 720px where the document-scale step would otherwise pull it back UP. The heading stays an `<h1>` either way — this moves the type step only, never the element, so the screen-reader outline is untouched.",
707
707
  "name": "headerScale",
708
- "type": "\"document\" | \"chrome\""
708
+ "type": "\"document\" | \"chrome\" | \"display\""
709
709
  },
710
710
  {
711
711
  "defaultValue": "\"default\"",
@@ -737,6 +737,16 @@
737
737
  "name": "headerLoading",
738
738
  "type": "boolean"
739
739
  },
740
+ {
741
+ "description": "The page's icon (an emoji or glyph) drawn large above the title, riding over a banner `cover`'s bottom edge — Notion's page icon (gh#1160). Add aria-hidden unless it is itself a control.",
742
+ "name": "icon",
743
+ "type": "ReactNode"
744
+ },
745
+ {
746
+ "description": "A PageCover: the banner variant spans the page edge to edge above the header; the eyecatch variant sits in the reading column above the title (gh#1160).",
747
+ "name": "cover",
748
+ "type": "ReactNode (a PageCover)"
749
+ },
740
750
  {
741
751
  "description": "Ref to the page's <h1>; passing it also makes the heading programmatically focusable (tabIndex -1, outside the tab order, no focus ring) so a router can focus the new page title after client-side navigation (gh#1135).",
742
752
  "name": "titleRef",
@@ -796,6 +806,81 @@
796
806
  "A chat channel page where a fixed band (channel workflow / pinned-message / connection status) must sit between the page header and the scrolling transcript — `toolbar={<Toolbar>…</Toolbar>}` with `fill` + `footer={<Composer/>}` + `stickyFooter`. The band is outside the scroller, so the transcript never travels under it and the composer stays pinned; a collection page uses the same slot for its filter strip above a full-bleed DataTable (`variant='flush'`)."
797
807
  ]
798
808
  },
809
+ {
810
+ "docPath": "docs/layout/page-cover.tsx",
811
+ "example": "import { PageContainer, PageCover } from \"@godxjp/ui/layout\";\nimport { Button } from \"@godxjp/ui/general\";\n\n<PageContainer\n title={page.title}\n headerScale=\"display\"\n icon={<span aria-hidden=\"true\">{page.icon}</span>}\n cover={\n <PageCover\n src={page.cover}\n alt=\"\"\n positionY={y}\n onPositionChange={setY}\n repositioning={moving}\n onRepositioningChange={setMoving}\n actions={<Button size=\"sm\" variant=\"secondary\" onClick={() => setMoving(true)}>Reposition</Button>}\n />\n }\n>\n {body}\n</PageContainer>",
812
+ "group": "layout",
813
+ "importPath": "@godxjp/ui/layout",
814
+ "name": "PageCover",
815
+ "props": [
816
+ {
817
+ "description": "Image URL.",
818
+ "name": "src",
819
+ "required": true,
820
+ "type": "string"
821
+ },
822
+ {
823
+ "description": "Alternative text; \"\" for a decorative cover.",
824
+ "name": "alt",
825
+ "required": true,
826
+ "type": "string"
827
+ },
828
+ {
829
+ "defaultValue": "50",
830
+ "description": "Vertical focal point, 0 top – 100 bottom.",
831
+ "name": "positionY",
832
+ "type": "number"
833
+ },
834
+ {
835
+ "description": "New focal point while repositioning.",
836
+ "name": "onPositionChange",
837
+ "type": "(positionY: number) => void"
838
+ },
839
+ {
840
+ "description": "Reposition mode (controlled): the image becomes a slider.",
841
+ "name": "repositioning",
842
+ "type": "boolean"
843
+ },
844
+ {
845
+ "description": "Enter / Escape ended the mode.",
846
+ "name": "onRepositioningChange",
847
+ "type": "(repositioning: boolean) => void"
848
+ },
849
+ {
850
+ "defaultValue": "\"banner\"",
851
+ "description": "Banner above the header, or eyecatch in the reading column.",
852
+ "name": "variant",
853
+ "type": "\"banner\" | \"eyecatch\""
854
+ },
855
+ {
856
+ "defaultValue": "\"md\"",
857
+ "description": "Banner height step; token-owned, scales phone → desktop.",
858
+ "name": "height",
859
+ "type": "\"md\" | \"lg\""
860
+ },
861
+ {
862
+ "description": "Change / reposition / remove controls: over the bottom edge on a wide screen, BELOW the image on a phone (never on the focal area).",
863
+ "name": "actions",
864
+ "type": "ReactNode"
865
+ },
866
+ {
867
+ "description": "Id on the cover frame.",
868
+ "name": "id",
869
+ "type": "string"
870
+ }
871
+ ],
872
+ "related": [
873
+ "PageContainer — `cover`, `icon`, headerScale=\"display\" for an article title.",
874
+ "Image — an image that opens a preview."
875
+ ],
876
+ "rules": [],
877
+ "storyPath": "layout/page-cover.tsx",
878
+ "tagline": "A page's cover image (gh#1160): Notion's banner across the top, or note.com's eyecatch (~1.91:1, rounded) in the reading column. Put it in PageContainer `cover`. The focal point is positionY (object-position); while `repositioning` the image IS a vertical WAI-ARIA slider — drag or arrows / PageUp / PageDown / Home / End, Enter / Escape to finish.",
879
+ "usage": [
880
+ "DO pass it as PageContainer `cover` with the page `icon` — the container places it and overlaps the icon.",
881
+ "DO wire a Reposition button in `actions` that sets `repositioning`, and persist positionY from onPositionChange."
882
+ ]
883
+ },
799
884
  {
800
885
  "example": "import { Flex } from \"@godxjp/ui/layout\";\nimport { Button } from \"@godxjp/ui/general\";\n\n<Flex direction=\"row\" gap=\"sm\" align=\"center\" justify=\"between\" wrap>\n <SearchSummary />\n <Flex direction=\"row\" gap=\"xs\" align=\"center\" wrap>\n <Button variant=\"outline\">リセット</Button>\n <Button>適用</Button>\n </Flex>\n</Flex>",
801
886
  "group": "layout",
@@ -5061,6 +5146,21 @@
5061
5146
  "name": "shortCellLength",
5062
5147
  "type": "number | false"
5063
5148
  },
5149
+ {
5150
+ "description": "Default 'same-tab'. Renderer policy for links to another origin (an absolute http(s) URL whose origin differs from `origin`). 'new-tab' adds target=\"_blank\" rel=\"noopener noreferrer\"; relative links, #anchors, resolved wikilinks and mailto: stay in the tab. Markdown cannot store target, so this replaces it.",
5151
+ "name": "externalLinks",
5152
+ "type": "'same-tab' | 'new-tab'"
5153
+ },
5154
+ {
5155
+ "description": "The page origin externalLinks compares against; defaults to location.origin. Pass it for server rendering, or every absolute link counts as external there.",
5156
+ "name": "origin",
5157
+ "type": "string"
5158
+ },
5159
+ {
5160
+ "description": "Visible titles for GitHub-alert callouts (`> [!WARNING]`) that carry none of their own. English by default (the package has no i18n); pass localized strings. Grammar v1 (gh#1156): callouts, `:::toggle[summary]`, `::::columns`/`:::column{width=N}`, CJK-friendly emphasis — the same syntax the pure codec at `@godxjp/markdown/codec` (CODEC_ID kit-md@1, parse/serialize/normalize, DOM-free) and @godxjp/block-editor use.",
5161
+ "name": "calloutTitles",
5162
+ "type": "Partial<Record<'note' | 'tip' | 'important' | 'warning' | 'caution', string>>"
5163
+ },
5064
5164
  {
5065
5165
  "defaultValue": "true",
5066
5166
  "description": "`false` keeps ```mermaid fences as code. Diagrams need the optional peer `mermaid`.",
@@ -5077,6 +5177,7 @@
5077
5177
  "storyPath": "data-display/markdown.tsx",
5078
5178
  "tagline": "The one Markdown renderer for GoDX apps (sibling package `@godxjp/markdown`, versioned with the kit): GFM + GitHub heading anchors, one sanitiser schema (no raw HTML; http/https/mailto/relative URLs only), and ```mermaid drawn only after its SVG passes a fail-closed gate (gh#1108). Wrap it in `Prose`.",
5079
5179
  "usage": [
5180
+ "Table of contents: outline(markdown) from @godxjp/markdown/codec returns { depth, text, id }[] with exactly the ids <Markdown> writes — feed it to the kit Anchor. YAML front matter is kept verbatim and never rendered; frontmatterOf(markdown) returns it (gh#1163).",
5080
5181
  "DO `<Prose><Markdown>{body}</Markdown></Prose>` for every rendered body (wiki page, issue, mail).",
5081
5182
  "DO record `MARKDOWN_FORMAT` and `RENDERER_VERSION` on stored versions.",
5082
5183
  "DON'T assemble react-markdown + rehype-sanitize per app, add rehype-raw, or render Mermaid yourself — `checkMermaidSvg` is the gate."
@@ -5148,6 +5249,97 @@
5148
5249
  "DON'T build a toolbar over a bare Textarea, or a rich-text surface — this is the shared editor."
5149
5250
  ]
5150
5251
  },
5252
+ {
5253
+ "docPath": "docs/data-entry/block-editor.tsx",
5254
+ "example": "import { BlockEditor } from \"@godxjp/block-editor\";\nimport { FormField } from \"@godxjp/ui/data-entry\";\n\n<FormField id=\"body\" label=\"本文\">\n <BlockEditor\n value={body}\n onValueChange={setBody}\n upload={async (file, { signal }) => ({ url: await media.put(file, { signal }), name: file.name })}\n suggestWikilinks={(q) => pages.search(q)}\n />\n</FormField>",
5255
+ "group": "data-entry",
5256
+ "importPath": "@godxjp/block-editor",
5257
+ "name": "BlockEditor",
5258
+ "props": [
5259
+ {
5260
+ "description": "Controlled Markdown body (with `onValueChange`).",
5261
+ "name": "value",
5262
+ "type": "string"
5263
+ },
5264
+ {
5265
+ "description": "Uncontrolled initial body.",
5266
+ "name": "defaultValue",
5267
+ "type": "string"
5268
+ },
5269
+ {
5270
+ "description": "Canonical Markdown after each edit — `serialize(editor.getJSON())`, identical to `normalize(body)` from `@godxjp/markdown/codec` (CODEC_ID kit-md@1).",
5271
+ "name": "onValueChange",
5272
+ "type": "(markdown: string) => void"
5273
+ },
5274
+ {
5275
+ "description": "Stores a pasted / dropped / chosen file (return `asset:<id>` if you like). An inline placeholder shows progress; failure offers Retry / Remove; deleting the placeholder aborts `signal`. Images become `![name](url)`, other files a link.",
5276
+ "name": "upload",
5277
+ "type": "(file: File, options: { signal: AbortSignal }) => Promise<{ url: string; name?: string }>"
5278
+ },
5279
+ {
5280
+ "description": "The host's media library; the `/` Image row opens it instead of a file picker.",
5281
+ "name": "pickMedia",
5282
+ "type": "() => Promise<{ url: string; name?: string } | null>"
5283
+ },
5284
+ {
5285
+ "description": "Refuse files with this message.",
5286
+ "name": "uploadBlockedReason",
5287
+ "type": "string | null"
5288
+ },
5289
+ {
5290
+ "description": "`[[` suggestions from the host's page index; picking inserts `[[target|label]]`.",
5291
+ "name": "suggestWikilinks",
5292
+ "type": "(query: string) => WikilinkSuggestion[] | Promise<WikilinkSuggestion[]>"
5293
+ },
5294
+ {
5295
+ "description": "Renders a block embed `![[target]]`; default is a labelled placeholder.",
5296
+ "name": "renderEmbed",
5297
+ "type": "(target: string) => ReactNode"
5298
+ },
5299
+ {
5300
+ "description": "Resolves a stored image URL (`asset:<id>`) for display; the body keeps the original.",
5301
+ "name": "resolveUrl",
5302
+ "type": "(url: string) => string | undefined"
5303
+ },
5304
+ {
5305
+ "defaultValue": "\"framed\"",
5306
+ "description": "framed: a field's border, padding and focus colour, so the editor reads as an editor inside a form; plain: the borderless Notion-style canvas for a full-page editor.",
5307
+ "name": "appearance",
5308
+ "type": "\"framed\" | \"plain\""
5309
+ },
5310
+ {
5311
+ "description": "Extra `/` rows ({ key, label, icon, keywords?, run(api) }) — `api.insertMarkdown` inserts through the codec.",
5312
+ "name": "actions",
5313
+ "type": "BlockEditorAction[]"
5314
+ },
5315
+ {
5316
+ "description": "Override any string (defaults: kit ja / en / vi).",
5317
+ "name": "labels",
5318
+ "type": "Partial<BlockEditorLabels>"
5319
+ },
5320
+ {
5321
+ "description": "Not editable; the body still renders as Prose.",
5322
+ "name": "disabled / readOnly",
5323
+ "type": "boolean"
5324
+ }
5325
+ ],
5326
+ "related": [
5327
+ "MarkdownEditor — a Textarea with Markdown tooling, for raw-Markdown editing.",
5328
+ "Markdown — rendering a stored body (same grammar, renderer v4)."
5329
+ ],
5330
+ "rules": [],
5331
+ "storyPath": "data-entry/block-editor.tsx",
5332
+ "tagline": "Notion / note.com style block editor (sibling package `@godxjp/block-editor`, versioned with the kit) that reads and writes MARKDOWN through `@godxjp/markdown/codec`: what onValueChange returns is byte-for-byte normalize(body). `/` block menu at the caret, ⋮⋮ handle (drag, turn into, duplicate, move, delete), floating format toolbar, `[[` wikilink suggestions, paste / drop uploads through the host, IME-safe, ja / en / vi (gh#1156).",
5333
+ "usage": [
5334
+ "Clicking a link (or Mod+K) opens a settings drawer: text, URL, title, open / remove. Clicking an image opens one for src, alt, title, replace (pickMedia / upload) and remove. Markdown stores no link target, so there is no target field. A link in the editor never navigates mid-edit.",
5335
+ "YAML front matter shows as a Properties block at the top and round-trips verbatim; read it with frontmatterOf() from @godxjp/markdown/codec.",
5336
+ "DO store the Markdown it emits — it is already canonical; compute `normalize()` server-side from the same codec to verify.",
5337
+ "DO give it a name: wrap in `FormField` or pass `aria-label` / `aria-labelledby` (the editable is a named `textbox`).",
5338
+ "DO pass `upload` (and `pickMedia` for a library) instead of handling paste yourself.",
5339
+ "DON'T render Markdown into it via HTML — pass `value`; HTML paste from other apps is handled by the schema.",
5340
+ "Keyboard: `/` menu (↑↓ Enter Esc), Alt+Shift+↑/↓ moves a block, Mod+/ opens the block menu, Markdown shortcuts (`# `, `- `, `> `, ```` ``` ````, `---`)."
5341
+ ]
5342
+ },
5151
5343
  {
5152
5344
  "example": "import { Prose } from \"@godxjp/ui/data-display\";\nimport Markdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\n\n<Prose size=\"sm\">\n <Markdown remarkPlugins={[remarkGfm]}>{issue.description}</Markdown>\n</Prose>",
5153
5345
  "group": "data-display",
@@ -11389,6 +11581,12 @@
11389
11581
  "name": "defaultExpandAll",
11390
11582
  "type": "boolean"
11391
11583
  },
11584
+ {
11585
+ "defaultValue": "false",
11586
+ "description": "Expand a parent by clicking its ROW, not only its switcher (antd DirectoryTree): \"click\" selects AND toggles — a page tree where choosing a folder opens it; \"doubleClick\" toggles on a double click (gh#1162). Keyboard keeps the APG map.",
11587
+ "name": "expandAction",
11588
+ "type": "false | \"click\" | \"doubleClick\""
11589
+ },
11392
11590
  {
11393
11591
  "description": "Lazy children (antd `loadData`). Called when a branch with no `children` and `isLeaf !== true` is expanded, and never again once its promise RESOLVES; a Skeleton row and `aria-busy` cover the wait. Push the fetched children into `treeData`. A REJECTED promise is not a load (rc-tree, gh#1041): the branch folds back shut (uncontrolled expansion) and the next expand asks again, up to 10 attempts.",
11394
11592
  "name": "loadData",
@@ -14177,6 +14375,11 @@
14177
14375
  "required": true,
14178
14376
  "type": "CommandPaletteGroup[]"
14179
14377
  },
14378
+ {
14379
+ "description": "Decorative glyph before an item's label — a folder, a file, an emoji (gh#1159). Menu-row icon size (--menu-icon-size), aria-hidden: the label stays the option's name.",
14380
+ "name": "groups[].items[].icon",
14381
+ "type": "ReactNode"
14382
+ },
14180
14383
  {
14181
14384
  "description": "Localized dialog and search copy.",
14182
14385
  "name": "labels",
@@ -14255,6 +14458,84 @@
14255
14458
  "The empty-state contract: with `shouldFilter` (default) cmdk decides — items exist, the query matches none. With `shouldFilter={false}` the PALETTE decides from props — `labels.empty` renders when `groups` carries no items, and never while `loading` or `error` is set. Hold `loading` for the whole in-flight window: a request that has not answered yet is not an empty result, and asserting over cmdk's own empty node races an async group that populates a frame late."
14256
14459
  ]
14257
14460
  },
14461
+ {
14462
+ "docPath": "docs/data-entry/emoji-picker.tsx",
14463
+ "example": "import { EmojiPicker } from \"@godxjp/ui/data-entry\";\n\n<EmojiPicker value={icon} onValueChange={setIcon} />",
14464
+ "group": "data-entry",
14465
+ "importPath": "@godxjp/ui/data-entry",
14466
+ "name": "EmojiPicker",
14467
+ "props": [
14468
+ {
14469
+ "description": "The chosen emoji (controlled with onValueChange).",
14470
+ "name": "value",
14471
+ "type": "string | null"
14472
+ },
14473
+ {
14474
+ "description": "Uncontrolled initial emoji.",
14475
+ "name": "defaultValue",
14476
+ "type": "string | null"
14477
+ },
14478
+ {
14479
+ "description": "Picked emoji, or null from \"remove icon\".",
14480
+ "name": "onValueChange",
14481
+ "type": "(emoji: string | null) => void"
14482
+ },
14483
+ {
14484
+ "description": "The popover.",
14485
+ "name": "open / defaultOpen / onOpenChange",
14486
+ "type": "boolean / boolean / (open) => void"
14487
+ },
14488
+ {
14489
+ "defaultValue": "true",
14490
+ "description": "Offer \"remove icon\" while a value is set.",
14491
+ "name": "removable",
14492
+ "type": "boolean"
14493
+ },
14494
+ {
14495
+ "defaultValue": "\"godxjp-ui:emoji-recents\"",
14496
+ "description": "localStorage key for the per-user recents.",
14497
+ "name": "recentsKey",
14498
+ "type": "string"
14499
+ },
14500
+ {
14501
+ "defaultValue": "\"md\"",
14502
+ "description": "Trigger size.",
14503
+ "name": "size",
14504
+ "type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
14505
+ },
14506
+ {
14507
+ "description": "Replace the default trigger (e.g. the page icon drawn large).",
14508
+ "name": "trigger",
14509
+ "type": "ReactNode"
14510
+ },
14511
+ {
14512
+ "description": "Disables the trigger.",
14513
+ "name": "disabled",
14514
+ "type": "boolean"
14515
+ },
14516
+ {
14517
+ "description": "Accessible name for the trigger; defaults to the localized \"Choose an icon\".",
14518
+ "name": "aria-label",
14519
+ "type": "string"
14520
+ },
14521
+ {
14522
+ "description": "Id on the trigger button.",
14523
+ "name": "id",
14524
+ "type": "string"
14525
+ }
14526
+ ],
14527
+ "related": [
14528
+ "PageContainer — `icon` slot.",
14529
+ "CommandPalette — items can carry an emoji `icon`."
14530
+ ],
14531
+ "rules": [],
14532
+ "storyPath": "data-entry/emoji-picker.tsx",
14533
+ "tagline": "Page icon / reaction picker in a Popover (gh#1164): search by the active language's CLDR keywords (ja / en / vi — emojibase-data, loaded lazily for that locale only), categories, per-user recents, a WAI-ARIA grid (one tab stop, arrows, Home / End, Enter), and \"remove icon\". No skin tones.",
14534
+ "usage": [
14535
+ "DO use it for a page icon next to PageContainer `icon` — pass the icon itself as `trigger`.",
14536
+ "The data loads on first open (one locale, ~100 KB gzip); nothing ships in your main bundle."
14537
+ ]
14538
+ },
14258
14539
  {
14259
14540
  "example": "import { TwoFactorSetup } from \"@godxjp/ui/feedback\";\n\n<TwoFactorSetup\n open={open}\n onOpenChange={setOpen}\n qrValue={enrollment.qr}\n manualKey={enrollment.secret}\n code={code}\n onCodeChange={setCode}\n onConfirm={verify}\n recoveryCodes={recoveryCodes}\n onAcknowledge={finish}\n labels={labels}\n/>",
14260
14541
  "group": "feedback",
package/agent/index.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "counts": {
3
3
  "anti-ai-tells": 26,
4
- "components": 181,
4
+ "components": 184,
5
5
  "patterns": 21,
6
6
  "rules": 50,
7
- "tokens": 2137,
7
+ "tokens": 2155,
8
8
  "vocabulary": 14
9
9
  },
10
10
  "files": [
11
11
  {
12
12
  "file": "components-index.json",
13
- "note": "50 KB — name + group + tagline for all 181. FETCH THIS FIRST, then fetch only the components you chose.",
13
+ "note": "51 KB — name + group + tagline for all 184. FETCH THIS FIRST, then fetch only the components you chose.",
14
14
  "url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json"
15
15
  },
16
16
  {
17
17
  "file": "components/<Name>.json",
18
- "note": "One file per component (1 KB–36 KB, median 6 KB), each carrying its importPath. This is the selective route: read the index, then fetch only what you need instead of the 1.2 MB blob.",
18
+ "note": "One file per component (1 KB–36 KB, median 6 KB), each carrying its importPath. This is the selective route: read the index, then fetch only what you need instead of the 1.3 MB blob.",
19
19
  "url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/<Name>.json"
20
20
  },
21
21
  {
@@ -48,19 +48,19 @@
48
48
  "note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
49
49
  "read": {
50
50
  "live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
51
- "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.26.2/agent/index.json"
51
+ "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.28.0/agent/index.json"
52
52
  },
53
53
  "source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
54
54
  "start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
55
55
  "tokenTiers": {
56
56
  "component": "per-part knobs, --{component}-{part}-{property}; usually leave these alone",
57
57
  "counts": {
58
- "component": 1822,
58
+ "component": 1832,
59
59
  "foundation": 211,
60
- "semantic": 104
60
+ "semantic": 112
61
61
  },
62
62
  "foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
63
63
  "semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
64
64
  },
65
- "version": "31.26.2"
65
+ "version": "31.28.0"
66
66
  }
package/agent/llms.txt CHANGED
@@ -1,10 +1,10 @@
1
1
  # @godxjp/ui
2
2
 
3
- > A Japanese-enterprise React design system: 181 components, 2137 design tokens,
4
- > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.26.2.
3
+ > A Japanese-enterprise React design system: 184 components, 2155 design tokens,
4
+ > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.28.0.
5
5
 
6
6
  If your client can run a process, do not read these files — run the MCP server instead
7
- (`npx @godxjp/ui-mcp@31.26.2`). It is searchable and version-locked. These files exist for agents
7
+ (`npx @godxjp/ui-mcp@31.28.0`). It is searchable and version-locked. These files exist for agents
8
8
  that can only fetch URLs.
9
9
 
10
10
  ## Start
@@ -15,10 +15,10 @@ that can only fetch URLs.
15
15
  ## Catalog
16
16
 
17
17
  - [patterns-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/patterns-index.json): 21 whole-task patterns (name, tagline, tags). Start here when the task is a TASK — "build a settings page" — then fetch `patterns/<name>.json` for complete code.
18
- - [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 50 KB — all 181 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
18
+ - [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 51 KB — all 184 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
19
19
  - [components/&lt;Name&gt;.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–36 KB, median 6 KB). Read the index, then fetch only the ones you chose — this is the selective route, and the reason you do not need the blob.
20
- - [components.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json): 1.2 MB — every entry in one file. Most URL fetchers truncate a response this size without saying so; prefer the per-component files.
21
- - [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 211 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 104 `semantic` roles, 1822 `component` knobs.
20
+ - [components.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json): 1.3 MB — every entry in one file. Most URL fetchers truncate a response this size without saying so; prefer the per-component files.
21
+ - [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 211 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 112 `semantic` roles, 1832 `component` knobs.
22
22
  - [vocabulary.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json): the controlled prop vocabulary — which prop name means what, across every component.
23
23
  - [rules.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json): 50 cardinal rules.
24
24
  - [anti-ai-tells.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/anti-ai-tells.json): 26 shapes that make generated UI look generated, each with its fix.
@@ -26,7 +26,7 @@ that can only fetch URLs.
26
26
  ## Pinning
27
27
 
28
28
  Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
29
- the tag: `.../godx-jp/godxjp-ui/v31.26.2/agent/...`. A catalog that does not match the installed
29
+ the tag: `.../godx-jp/godxjp-ui/v31.28.0/agent/...`. A catalog that does not match the installed
30
30
  package describes props that are absent, or hides props that are present, and says nothing either way.
31
31
 
32
32
  Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
package/agent/tokens.json CHANGED
@@ -1745,6 +1745,54 @@
1745
1745
  "tier": "semantic",
1746
1746
  "value": "var(--heading-h3, var(--font-size-base))"
1747
1747
  },
1748
+ {
1749
+ "description": "PageContainer `headerScale=\"display\"` (gh#1160) — an ARTICLE's headline (a Notion / note.com page). The 4xl display step, which already scales with the viewport through `--font-size-display`'s clamp (≈25px on a phone → 42px wide), so one knob covers phone and desktop and the compact step below does not apply. CJK titles need more leading than Latin ones at this size: 1.4, not the tight 1.25 a short Latin headline gets.",
1750
+ "name": "--page-title-font-size-display",
1751
+ "tier": "semantic",
1752
+ "value": "var( --font-size-4xl, calc(var(--font-size-display) / var(--font-size-display-ratio)) )"
1753
+ },
1754
+ {
1755
+ "description": "Layout primitive tokens: page, section, stack, inline.",
1756
+ "name": "--page-title-line-height-display",
1757
+ "tier": "semantic",
1758
+ "value": "1.4"
1759
+ },
1760
+ {
1761
+ "description": "The page icon above the title (gh#1160): emoji or glyph, and how far it rides up over a banner cover's bottom edge.",
1762
+ "name": "--page-icon-size",
1763
+ "tier": "semantic",
1764
+ "value": "var(--icon-size-4xl)"
1765
+ },
1766
+ {
1767
+ "description": "Layout primitive tokens: page, section, stack, inline.",
1768
+ "name": "--page-icon-cover-overlap",
1769
+ "tier": "semantic",
1770
+ "value": "calc(var(--page-icon-size) * 0.6)"
1771
+ },
1772
+ {
1773
+ "description": "PageCover (gh#1160). A banner's height scales with the viewport between a phone floor and a desktop ceiling — one knob per step, no media query; an eyecatch keeps note.com's ~1.91:1.",
1774
+ "name": "--page-cover-block-size-md",
1775
+ "tier": "semantic",
1776
+ "value": "clamp(8rem, 24vw, 15rem)"
1777
+ },
1778
+ {
1779
+ "description": "Layout primitive tokens: page, section, stack, inline.",
1780
+ "name": "--page-cover-block-size-lg",
1781
+ "tier": "semantic",
1782
+ "value": "clamp(10rem, 32vw, 20rem)"
1783
+ },
1784
+ {
1785
+ "description": "Layout primitive tokens: page, section, stack, inline.",
1786
+ "name": "--page-cover-eyecatch-aspect-ratio",
1787
+ "tier": "semantic",
1788
+ "value": "1.91"
1789
+ },
1790
+ {
1791
+ "description": "default = var(--card-radius) at the call site (gh#888)",
1792
+ "name": "--page-cover-radius",
1793
+ "tier": "semantic",
1794
+ "value": "initial"
1795
+ },
1748
1796
  {
1749
1797
  "description": "The SUBTITLE step of the same fact. Measured against the reference chat screen, the chrome title and the chrome subtitle were rendering at the SAME step (both --font-size-base), which is not a hierarchy — a channel name and the channel's purpose line read as one undifferentiated block. The design puts the purpose line two steps down (`Text size=\"2xs\"`), and the size is what carries the LINE BOX: at 14px/1.7 the subtitle line is 23.8px tall, at 11px/1.7 it is 18.9px, so the whole header band loses ~5px it was spending on a caption. `--font-size-2xs` (ratio⁻², ≈11px) is that step, and it is the same golden scale the title step reads — never a literal. Quiet by default: a page that never passes `headerScale` never resolves it (#44).",
1750
1798
  "name": "--page-subtitle-font-size-chrome",
@@ -4835,6 +4883,30 @@
4835
4883
  "tier": "component",
4836
4884
  "value": "initial"
4837
4885
  },
4886
+ {
4887
+ "description": "EmojiPicker (gh#1164): the panel width, an emoji cell (a touch-sized square), the glyph inside it, and how tall the grid gets before it scrolls.",
4888
+ "name": "--emoji-picker-cell-size",
4889
+ "tier": "component",
4890
+ "value": "var(--control-height-default)"
4891
+ },
4892
+ {
4893
+ "description": "Eight cells plus the popover's inset — derived, so 44px touch cells (coarse pointer) still fit eight to a row (8 × 44 + insets = 376px, inside a 390px phone).",
4894
+ "name": "--emoji-picker-panel-inline-size",
4895
+ "tier": "component",
4896
+ "value": "calc( 8 * var(--emoji-picker-cell-size) + 2 * var(--popover-space-inset, var(--space-3)) )"
4897
+ },
4898
+ {
4899
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
4900
+ "name": "--emoji-picker-glyph-size",
4901
+ "tier": "component",
4902
+ "value": "var(--icon-size-lg)"
4903
+ },
4904
+ {
4905
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
4906
+ "name": "--emoji-picker-grid-max-block-size",
4907
+ "tier": "component",
4908
+ "value": "16rem"
4909
+ },
4838
4910
  {
4839
4911
  "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
4840
4912
  "name": "--control-height-compact",
@@ -5231,6 +5303,42 @@
5231
5303
  "tier": "component",
5232
5304
  "value": "20rem"
5233
5305
  },
5306
+ {
5307
+ "description": "Grammar v1 blocks (gh#1156). Columns sit this far apart and stack below 40rem; a toggle's summary is the control that opens it. Callouts reuse the <Callout> geometry (--callout-*).",
5308
+ "name": "--prose-columns-gap",
5309
+ "tier": "component",
5310
+ "value": "var(--space-6)"
5311
+ },
5312
+ {
5313
+ "description": "@godxjp/block-editor (gh#1156): the start gutter the ⋮⋮ / + handle sits in, and the editing surface's minimum height (an empty body is still a target you can click into).",
5314
+ "name": "--block-editor-gutter-inline-size",
5315
+ "tier": "component",
5316
+ "value": "calc(2 * var(--control-height-xs) + var(--space-2))"
5317
+ },
5318
+ {
5319
+ "description": "Data-display component tokens — small-by-design text knobs (rule #45/#46).",
5320
+ "name": "--block-editor-min-height",
5321
+ "tier": "component",
5322
+ "value": "var(--band-height-3xl)"
5323
+ },
5324
+ {
5325
+ "description": "`appearance=\"framed\"`: the inset between the frame and the text (the handle gutter is extra).",
5326
+ "name": "--block-editor-frame-padding",
5327
+ "tier": "component",
5328
+ "value": "var(--space-3)"
5329
+ },
5330
+ {
5331
+ "description": "Data-display component tokens — small-by-design text knobs (rule #45/#46).",
5332
+ "name": "--block-editor-menu-inline-size",
5333
+ "tier": "component",
5334
+ "value": "16rem"
5335
+ },
5336
+ {
5337
+ "description": "Data-display component tokens — small-by-design text knobs (rule #45/#46).",
5338
+ "name": "--block-editor-menu-max-block-size",
5339
+ "tier": "component",
5340
+ "value": "20rem"
5341
+ },
5234
5342
  {
5235
5343
  "description": "gh#888 — φ tier freezes at :root; default = calc(var(--radius) / var(--radius-ratio) / var(--radius-ratio))",
5236
5344
  "name": "--prose-code-radius",
@@ -63,6 +63,7 @@ function TreeRoot({
63
63
  defaultExpandedValues,
64
64
  onExpandedValuesChange,
65
65
  defaultExpandAll = false,
66
+ expandAction = false,
66
67
  loadData,
67
68
  titleRender,
68
69
  filterTreeNode,
@@ -491,6 +492,10 @@ function TreeRoot({
491
492
  onClick: () => {
492
493
  if (nodeDisabled) return;
493
494
  select(node);
495
+ if (expandAction === "click" && expandable) toggleExpand(node);
496
+ },
497
+ onDoubleClick: () => {
498
+ if (!nodeDisabled && expandAction === "doubleClick" && expandable) toggleExpand(node);
494
499
  },
495
500
  "data-value": node.value,
496
501
  "data-selected": isSelected ? "true" : void 0,
@@ -2,6 +2,11 @@ import * as React from "react";
2
2
  export type CommandPaletteItem = {
3
3
  id: string;
4
4
  label: React.ReactNode;
5
+ /**
6
+ * Decorative glyph before the label — a folder, a file, an emoji (gh#1159). Sized like a menu
7
+ * row's icon (`--menu-icon-size`) and hidden from assistive tech: the label is the name.
8
+ */
9
+ icon?: React.ReactNode;
5
10
  searchValue?: string;
6
11
  meta?: React.ReactNode;
7
12
  disabled?: boolean;
@@ -189,6 +189,7 @@ function CommandPalette({
189
189
  onSelect(item, modifiers);
190
190
  },
191
191
  children: [
192
+ item.icon != null ? /* @__PURE__ */ jsx("span", { className: "ui-command-palette-icon", "aria-hidden": "true", children: item.icon }) : null,
192
193
  /* @__PURE__ */ jsx("span", { className: "ui-command-palette-label", children: item.label }),
193
194
  item.meta != null ? /* @__PURE__ */ jsx("span", { className: "ui-command-palette-meta", children: item.meta }) : null
194
195
  ]