@hjmds/design-contracts 1.34.0 → 1.35.1

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 (158) hide show
  1. package/dist/anchor.d.ts +4 -1
  2. package/dist/anchor.d.ts.map +1 -1
  3. package/dist/anchor.js +3 -3
  4. package/dist/anchor.js.map +1 -1
  5. package/dist/base-recipes.d.ts +15 -1
  6. package/dist/base-recipes.d.ts.map +1 -1
  7. package/dist/base-recipes.js +14 -3
  8. package/dist/base-recipes.js.map +1 -1
  9. package/dist/behaviors.d.ts +22 -0
  10. package/dist/behaviors.d.ts.map +1 -1
  11. package/dist/behaviors.js +2 -0
  12. package/dist/behaviors.js.map +1 -1
  13. package/dist/calendar.d.ts +9 -7
  14. package/dist/calendar.d.ts.map +1 -1
  15. package/dist/calendar.js +31 -2
  16. package/dist/calendar.js.map +1 -1
  17. package/dist/card.d.ts +15 -1
  18. package/dist/card.d.ts.map +1 -1
  19. package/dist/card.js +14 -1
  20. package/dist/card.js.map +1 -1
  21. package/dist/carousel.d.ts +3 -0
  22. package/dist/carousel.d.ts.map +1 -1
  23. package/dist/carousel.js +10 -1
  24. package/dist/carousel.js.map +1 -1
  25. package/dist/catalog.d.ts +147 -17
  26. package/dist/catalog.d.ts.map +1 -1
  27. package/dist/catalog.js +9 -6
  28. package/dist/catalog.js.map +1 -1
  29. package/dist/colors.d.ts +1 -0
  30. package/dist/colors.d.ts.map +1 -1
  31. package/dist/colors.js +2 -0
  32. package/dist/colors.js.map +1 -1
  33. package/dist/component-recipes.d.ts +45 -11
  34. package/dist/component-recipes.d.ts.map +1 -1
  35. package/dist/component-recipes.js +35 -15
  36. package/dist/component-recipes.js.map +1 -1
  37. package/dist/component-references.d.ts +2 -2
  38. package/dist/component-references.js +1 -1
  39. package/dist/component-references.js.map +1 -1
  40. package/dist/design-profile.d.ts +14 -0
  41. package/dist/design-profile.d.ts.map +1 -1
  42. package/dist/design-profile.js +15 -0
  43. package/dist/design-profile.js.map +1 -1
  44. package/dist/index.d.ts +2 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +2 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/progress-recipe.d.ts +6 -0
  49. package/dist/progress-recipe.d.ts.map +1 -1
  50. package/dist/progress-recipe.js +15 -2
  51. package/dist/progress-recipe.js.map +1 -1
  52. package/dist/recipes.d.ts +2 -2
  53. package/dist/recipes.d.ts.map +1 -1
  54. package/dist/recipes.js +2 -2
  55. package/dist/recipes.js.map +1 -1
  56. package/dist/screen-patterns.d.ts +32 -2
  57. package/dist/screen-patterns.d.ts.map +1 -1
  58. package/dist/screen-patterns.js +26 -2
  59. package/dist/screen-patterns.js.map +1 -1
  60. package/dist/semantic-colors.d.ts +5 -0
  61. package/dist/semantic-colors.d.ts.map +1 -1
  62. package/dist/semantic-colors.js +2 -0
  63. package/dist/semantic-colors.js.map +1 -1
  64. package/dist/sheet.d.ts +15 -0
  65. package/dist/sheet.d.ts.map +1 -1
  66. package/dist/sheet.js +11 -0
  67. package/dist/sheet.js.map +1 -1
  68. package/dist/shortcut-rail.d.ts +20 -0
  69. package/dist/shortcut-rail.d.ts.map +1 -0
  70. package/dist/shortcut-rail.js +28 -0
  71. package/dist/shortcut-rail.js.map +1 -0
  72. package/dist/showcase.js +2 -2
  73. package/dist/showcase.js.map +1 -1
  74. package/dist/steps.d.ts +6 -4
  75. package/dist/steps.d.ts.map +1 -1
  76. package/dist/steps.js +3 -0
  77. package/dist/steps.js.map +1 -1
  78. package/dist/time-picker.d.ts +70 -0
  79. package/dist/time-picker.d.ts.map +1 -0
  80. package/dist/time-picker.js +51 -0
  81. package/dist/time-picker.js.map +1 -0
  82. package/dist/timeline.d.ts +13 -1
  83. package/dist/timeline.d.ts.map +1 -1
  84. package/dist/timeline.js +11 -1
  85. package/dist/timeline.js.map +1 -1
  86. package/dist/version.d.ts +1 -1
  87. package/dist/version.js +1 -1
  88. package/dist/version.js.map +1 -1
  89. package/docs/calendar.md +19 -1
  90. package/docs/carousel.md +10 -0
  91. package/docs/collapsible.md +9 -0
  92. package/docs/consumer-policy.md +8 -12
  93. package/docs/design-profile.md +9 -0
  94. package/docs/expansion-roadmap.md +1 -1
  95. package/docs/generated/component-maturity.md +5 -4
  96. package/docs/generated/renderer-evidence.json +141 -16
  97. package/docs/generated/renderer-evidence.md +5 -3
  98. package/docs/generated/showcase-manifest.json +69 -7
  99. package/docs/optional-adapters.md +11 -0
  100. package/docs/stable-core.md +25 -0
  101. package/docs/steps.md +4 -0
  102. package/docs/theme-palette.md +2 -0
  103. package/docs/time-picker.md +22 -114
  104. package/docs/usage/README.md +9 -7
  105. package/docs/usage/SELECTION.md +44 -2
  106. package/docs/usage/components/accordion.md +4 -2
  107. package/docs/usage/components/alert-dialog.md +5 -0
  108. package/docs/usage/components/anchor.md +4 -1
  109. package/docs/usage/components/badge.md +7 -4
  110. package/docs/usage/components/bottom-cta.md +29 -4
  111. package/docs/usage/components/bottom-navigation.md +8 -0
  112. package/docs/usage/components/button.md +42 -2
  113. package/docs/usage/components/calendar.md +8 -1
  114. package/docs/usage/components/card.md +111 -7
  115. package/docs/usage/components/carousel.md +4 -1
  116. package/docs/usage/components/chat-screen.md +1 -1
  117. package/docs/usage/components/checkbox-group.md +33 -3
  118. package/docs/usage/components/checkbox.md +8 -2
  119. package/docs/usage/components/chip.md +5 -0
  120. package/docs/usage/components/collapsible.md +16 -1
  121. package/docs/usage/components/comment-thread-screen.md +12 -3
  122. package/docs/usage/components/composition-layout.md +6 -1
  123. package/docs/usage/components/conversation-row.md +2 -1
  124. package/docs/usage/components/data-table.md +10 -0
  125. package/docs/usage/components/description-list.md +1 -0
  126. package/docs/usage/components/field.md +8 -0
  127. package/docs/usage/components/icon-tile.md +29 -10
  128. package/docs/usage/components/image-choice-group.md +6 -0
  129. package/docs/usage/components/image.md +8 -1
  130. package/docs/usage/components/list-detail-screen.md +10 -4
  131. package/docs/usage/components/list-row.md +3 -0
  132. package/docs/usage/components/list.md +33 -1
  133. package/docs/usage/components/message-composer.md +2 -0
  134. package/docs/usage/components/notice.md +5 -1
  135. package/docs/usage/components/notification-row.md +19 -9
  136. package/docs/usage/components/onboarding-screen.md +3 -0
  137. package/docs/usage/components/page-indicator.md +12 -0
  138. package/docs/usage/components/profile-summary.md +16 -3
  139. package/docs/usage/components/progress.md +24 -0
  140. package/docs/usage/components/radio-group.md +45 -3
  141. package/docs/usage/components/radio.md +8 -2
  142. package/docs/usage/components/repeatable-field-group.md +1 -0
  143. package/docs/usage/components/screen-layout.md +46 -3
  144. package/docs/usage/components/search-screen.md +29 -1
  145. package/docs/usage/components/segmented-control.md +19 -3
  146. package/docs/usage/components/select.md +2 -0
  147. package/docs/usage/components/sheet.md +56 -3
  148. package/docs/usage/components/spinner.md +10 -0
  149. package/docs/usage/components/statistic.md +23 -0
  150. package/docs/usage/components/steps.md +11 -4
  151. package/docs/usage/components/switch.md +5 -0
  152. package/docs/usage/components/tabs.md +4 -0
  153. package/docs/usage/components/time-picker.md +88 -0
  154. package/docs/usage/components/timeline.md +19 -6
  155. package/docs/usage/components/top-bar.md +7 -0
  156. package/docs/usage/compositions/shortcut-rail.md +99 -0
  157. package/docs/usage/compositions/time-selection.md +5 -0
  158. package/package.json +17 -4
@@ -1 +1 @@
1
- {"version":3,"file":"screen-patterns.js","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { glyph, layout, radius, spacing } from \"./foundations.js\";\nimport { avatarRecipe, emptyStateRecipe } from \"./component-recipes.js\";\nimport type { MenuItemDescriptor } from \"./behaviors.js\";\n\n/** Initial load replaces the body; refresh/save errors belong beside retained content. */\nexport type ScreenContentState =\n | Readonly<{ kind: \"ready\" }>\n | Readonly<{ kind: \"loading\" | \"empty\" | \"error\" | \"restricted\"; title: string; description?: string }>;\n\nexport function resolveScreenContentState(state: ScreenContentState = { kind: \"ready\" }) {\n if (![\"ready\", \"loading\", \"empty\", \"error\", \"restricted\"].includes(state.kind)) {\n throw new TypeError(\"Unknown screen content state\");\n }\n if (state.kind !== \"ready\" && !state.title.trim()) {\n throw new TypeError(\"Screen state requires a localized title\");\n }\n return {\n kind: state.kind,\n replacesContent: state.kind !== \"ready\",\n busy: state.kind === \"loading\",\n // 2026-10-18: empty copy stays at the 14pt body size, including full-screen compositions.\n titleVariant: state.kind === \"empty\" ? emptyStateRecipe.title.textVariant : \"title\",\n announcement: state.kind === \"error\" ? \"assertive\" : \"polite\",\n } as const;\n}\n\n/**\n * Native header band heights. Titled centered headers match the iOS navigation bar (44 control + 4 + 4);\n * icon-only centered headers are compact (2026-10-14 야잘알 tab spacing, 2026-10-17 \"상단 여백 줄여\").\n * Body top padding shrinks only under a compact header without a notice.\n */\nexport const screenHeaderRecipe = {\n minHeight: 52,\n paddingVertical: spacing.xxs,\n compactMinHeight: 48,\n compactPaddingVertical: 2,\n compactBodyTop: spacing.xxs,\n /**\n * Heading levels for the route title, shared by Web and Native (2026-10-17 comparison: Native drew the\n * title as regular-weight Text — 20 start / 18 centered — while Web used bold Heading levels).\n */\n titleLevel: \"level3\",\n centeredTitleLevel: \"level5\",\n} as const;\n\n/**\n * Leaving a screen (back, close, \"설정 나가기\") is an icon-only button fixed at the header's leading edge\n * (2026-10-17 user rule: exits had drifted into body text buttons with mixed glyphs). The label is read\n * by assistive tech only. `back` mirrors in RTL; `close` is for modal-style routes.\n */\nexport type ScreenExitKind = \"back\" | \"close\";\nexport const screenExitRecipe = {\n /** Chevron arm length and stroke; close cross arm length. Drawn, not font glyphs, so every platform matches. */\n chevronSize: 10,\n closeSize: 14,\n stroke: 2,\n} as const;\nexport function validateScreenExit(exit: Readonly<{ kind: string; label: string }>, hasLeading: boolean) {\n if (exit.kind !== \"back\" && exit.kind !== \"close\") throw new TypeError(\"Screen exit kind must be back or close\");\n if (!exit.label.trim()) throw new TypeError(\"Screen exit needs a localized accessible label\");\n if (hasLeading) throw new TypeError(\"Screen exit owns the leading slot; do not also pass leading\");\n}\n\n/**\n * Who owns the screen gutters. `default`: the shared shell pads header, notice, body and footer. `none`: the host\n * route already insets everything (≈45 product call sites rely on this meaning, so it cannot change).\n * `body-edge`: header, notice and footer keep the shell padding; only the body's inline padding is 0 so full-width\n * list rows (ConversationRow, ListRow-based items) supply their own inset — review S5#9 measured DM avatars ~32pt\n * from the edge because body padding and row padding stacked (2026-10-17 user decision).\n * `body-bleed`: header, notice and footer keep the shell padding; the body has no gutter on any side, so a map or\n * canvas meets the header (or notice), the screen edges and the footer. With no footer the body is still the last\n * region and clears the bottom insets the screen owns (`safeAreaBottom`, an overlay BottomNavigation) without the\n * gutter. choose-window's map screens used `body-edge` and kept a 16pt strip above and below the map (2026-10-18).\n */\nexport type ScreenContentInset = \"default\" | \"none\" | \"body-edge\" | \"body-bleed\";\nexport const screenContentInsets = [\"default\", \"none\", \"body-edge\", \"body-bleed\"] as const satisfies readonly ScreenContentInset[];\n\n/**\n * 2026-10-18 사용자 지시: 입력 자동 포커스는 페이지 진입에만 적용하고 하단 시트에서는 폐지한다.\n * 페이지는 `first-input`으로 명시한 주 입력 또는 첫 유효 입력을 한 번 고른다. 하단 시트는 renderer의\n * 상속 경계에서 중첩 화면까지 자동 포커스를 막는다. Dialog·측면 시트의 모달 포커스 계약은 유지한다.\n * `single-input`은 Dialog·측면 시트의 단일 입력 선택, `none`은 입력 자동 선택을 끈다.\n * 비활성·읽기 전용·initialFocusTarget={false} 칸은 제외한다. 이미 포커스된 입력을 빼앗지 않으며\n * 화면은 mount당 한 번만 포커스한다. 모션 감소는 포커스 정책을 바꾸지 않는다.\n */\nexport type InitialFocusMode = \"first-input\" | \"single-input\" | \"none\";\nexport type ScreenInitialFocus = \"first-input\" | \"none\";\nexport type InitialFocusCandidate = Readonly<{\n /** Registration order inside the scope (tree order). */\n order: number;\n /** `initialFocusTarget` on the field: true prefers it, false excludes it, undefined keeps it eligible. */\n preference: boolean | undefined;\n /** False for disabled or read-only fields at resolve time. */\n focusable: boolean;\n}>;\n\n/** Defaults per composition; renderers read these instead of repeating literals. */\nexport const initialFocusDefaults = {\n // 2026-10-18: input focus belongs to page entry, including chat/auth pages; bottom sheets opt out at their boundary.\n screen: \"first-input\",\n search: \"first-input\",\n editor: \"first-input\",\n chat: \"first-input\",\n commentThread: \"first-input\",\n overlay: \"single-input\",\n auth: \"first-input\",\n} as const satisfies Record<string, InitialFocusMode>;\n\nexport function resolveInitialFocusTarget<T extends InitialFocusCandidate>(candidates: readonly T[], mode: InitialFocusMode): T | null {\n if (mode === \"none\") return null;\n const ordered = [...candidates].sort((a, b) => a.order - b.order);\n if (mode === \"single-input\") {\n const only = ordered.length === 1 ? ordered[0]! : null;\n return only && only.focusable && only.preference !== false ? only : null;\n }\n const eligible = ordered.filter(candidate => candidate.focusable && candidate.preference !== false);\n return eligible.find(candidate => candidate.preference === true) ?? eligible[0] ?? null;\n}\n\n/**\n * 진입 포커스 창(2026-10-18 1.28.1 앱 채택 점검). mount 시점 한 번만 고르면 세 경우가 앱 우회로 남았다: 진입 때 열린\n * 모달 아래 칸(번뚝 첫 가입 약관), 진입 중 비동기로 나타나는 칸(번뚝 계정 삭제 확인), 사용자 행동으로 나중에 나타나는 칸\n * (utilverse 검색 열기, 번뚝 아이디어 시트·spint 작성 단계). 그래서 범위는 아래 상태를 가진다.\n *\n * - `entering`: 진입 중(Native 전환·시트 등장 interaction 전). 칸 등록만 받는다.\n * - `open`: 진입이 끝났지만 아직 포커스하지 못했다. 칸이 새로 등록되거나 위 오버레이가 닫히면 다시 고른다. 모달 아래에서\n * 진입한 화면은 모달이 닫힐 때 사용자가 그 화면을 아직 만지지 않았으면 그때 포커스한다.\n * - `settled`: 포커스했거나, 사용자가 범위를 직접 조작(터치·포인터·키·휠)했거나, 이미 다른 텍스트 칸에 초점이 있다.\n * 이후에 입력이 새로 나타나도 자동 포커스를 다시 시작하지 않는다(2026-10-18 페이지 진입에만 적용).\n *\n * 위에 다른 오버레이가 열려 있는 동안(`blocked`)은 고르지 않고, 그 동안의 조작은 아래 범위의 조작으로 세지 않는다\n * (약관 동의 탭이 화면의 진입 포커스를 끝내면 안 된다). 텍스트 칸에 이미 초점이 있으면 빼앗지 않는다.\n */\nexport type EntryFocusPhase = \"inactive\" | \"entering\" | \"open\" | \"settled\";\nexport type EntryFocusEvent<T extends InitialFocusCandidate> =\n /** The scope mounted (screen) or opened (overlay). */\n | Readonly<{ kind: \"activate\" }>\n /** The overlay closed or the scope unmounted. */\n | Readonly<{ kind: \"deactivate\" }>\n /** The enter transition finished. */\n | Readonly<{ kind: \"enter\" }>\n /** A field mounted into the scope (first registration of that field instance). */\n | Readonly<{ kind: \"register\"; field: T }>\n /** An overlay above the scope closed. */\n | Readonly<{ kind: \"unblock\" }>\n /** User touch, pointer, key or wheel input reached the scope. */\n | Readonly<{ kind: \"interact\" }>;\nexport type EntryFocusFacts<T extends InitialFocusCandidate> = Readonly<{\n mode: InitialFocusMode;\n /** Every field registered in the scope now, including a newly registered one. */\n candidates: readonly T[];\n /** Another overlay is open above this scope. */\n blocked: boolean;\n /** A text field already has focus (the user's or the app's). */\n textEntryFocused: boolean;\n}>;\nexport type EntryFocusResolution<T> = Readonly<{ phase: EntryFocusPhase; target: T | null }>;\n\nexport function resolveEntryFocus<T extends InitialFocusCandidate>(phase: EntryFocusPhase, event: EntryFocusEvent<T>, facts: EntryFocusFacts<T>): EntryFocusResolution<T> {\n const stay = { phase, target: null };\n if (event.kind === \"deactivate\") return { phase: \"inactive\", target: null };\n if (event.kind === \"activate\") return { phase: \"entering\", target: null };\n if (phase === \"inactive\") return stay;\n if (event.kind === \"interact\") return facts.blocked || phase === \"settled\" ? stay : { phase: \"settled\", target: null };\n const attempt = (): EntryFocusResolution<T> => {\n if (facts.blocked) return { phase: \"open\", target: null };\n if (facts.textEntryFocused) return { phase: \"settled\", target: null };\n const target = resolveInitialFocusTarget(facts.candidates, facts.mode);\n return target ? { phase: \"settled\", target } : { phase: \"open\", target: null };\n };\n if (event.kind === \"enter\") return phase !== \"entering\" ? stay : facts.mode === \"none\" ? { phase: \"settled\", target: null } : attempt();\n if (phase === \"entering\") return stay;\n if (phase === \"open\") return attempt();\n // Page entry only: revealing a marked field after interaction/entry must not open the keyboard again.\n return stay;\n}\n\n// Reuse reading-width and spacing tokens; do not derive geometry from a product's device or artwork.\nexport const screenPatternRecipe = {\n maxWidth: layout.readingMaxWidth,\n padding: spacing.md,\n sectionGap: spacing.xl,\n itemGap: spacing.sm,\n stateGap: spacing.md,\n // Compact screens must keep back/title/save on one row; scale this floor for large text instead of reserving a desktop-width title.\n headerMinWidth: layout.readingMaxWidth / 6,\n // Reserve the opposite edge for authorship and cap the composer so history remains visible.\n messageMaxWidth: \"84%\",\n // Social thumbnails stay compact; selection order and removal replace a second toolbar.\n attachmentSize: 80,\n attachmentRemoveSize: 24,\n reactionHoldMs: 450,\n reactionMoveTolerance: 10,\n reactionMenuWidth: 320,\n replySwipeDistance: 60,\n composerMinLines: 1,\n composerMaxLines: 5,\n} as const;\n\n/** Controlled composer: HJM never clears drafts or interprets a fulfilled callback as server delivery. */\nexport type MessageComposerDescriptor = Readonly<{\n value: string;\n label: string;\n sendLabel: string;\n /** Short visual copy may differ from the complete accessible input name. */\n placeholder?: string;\n description?: string;\n error?: string;\n invalid?: boolean;\n /** Newline remains the compatible default; desktop chat may explicitly opt into send. */\n submitMode?: \"newline\" | \"send\";\n disabled?: boolean;\n pending?: boolean;\n attachmentCount?: number;\n}>;\n/** Safari may report the final IME Enter as keyCode 229 after isComposing becomes false. */\nexport function shouldSubmitMessageKey(event: Readonly<{ key: string; shiftKey?: boolean; isComposing?: boolean; keyCode?: number }>, mode: \"newline\" | \"send\" = \"newline\"): boolean {\n return mode === \"send\" && event.key === \"Enter\" && !event.shiftKey && !event.isComposing && event.keyCode !== 229;\n}\nexport function canSubmitMessage({ value, disabled = false, pending = false, attachmentCount = 0 }: MessageComposerDescriptor): boolean {\n if (!Number.isSafeInteger(attachmentCount) || attachmentCount < 0) throw new RangeError(\"Attachment count must be a nonnegative integer\");\n return !disabled && !pending && (value.trim().length > 0 || attachmentCount > 0);\n}\n\n/**\n * Shared timeline geometry. 2026-10-16 user decision: the common chat standard follows the measured\n * KakaoTalk iOS chat room (@3x capture: bubble ~35pt tall, radius ~14, run gap ~6, group gap ~12,\n * avatar ~34, avatar gap 8). Brand colors and artwork are not copied; surfaces stay semantic tokens.\n */\nexport const chatMessageRecipe = {\n // Must be a size the Avatar renders on both platforms: 36 was no Web Avatar step (32/40/48/64), so Web drew a 32\n // avatar in a 36 slot while Native drew 36 (2026-10-17 measured audit). `small` is the step nearest the ~34 reference.\n avatarSize: avatarRecipe.sizes.small,\n avatarGap: spacing.xs,\n authorGap: spacing.xxs,\n metaGap: spacing.xxs,\n bubbleRadius: 14,\n bubblePaddingInline: 10,\n bubblePaddingBlock: 6,\n // Bubbles of one run read as a stack; a new author or minute starts a visibly separate group.\n runGap: 6,\n groupGap: spacing.sm,\n // Flat-topped tip continuing the bubble's top edge; the tipped corner tightens to meet it.\n tailWidth: 7,\n tailHeight: 10,\n tailCornerRadius: 4,\n jumpToLatestGap: spacing.xs,\n // Timeline side gutter (measured 10pt); header and composer keep the screen padding.\n timelineInset: 10,\n // Photos render without the bubble surface (measured reference: ~210pt wide, bubble radius).\n mediaWidth: 210,\n mediaGap: 2,\n /**\n * Reaction summary pill beside the time (2026-10-18 BurnTok DM): one button that opens the same hold menu.\n * Counts below `minCount` show the emoji alone, as the measured DM references do for a single reaction.\n */\n reactionSummary: { minHeight: 24, paddingInline: spacing.xs, itemGap: spacing.xxs, emojiGap: 2, minCount: 2 },\n} as const;\n\nexport type ChatReactionSummaryItem = Readonly<{ id: string; emoji: string; count: number }>;\nexport type ChatReactionSummary = Readonly<{\n label: string;\n items: readonly ChatReactionSummaryItem[];\n /**\n * Counts only, for readers who cannot react (2026-10-18 Utilverse guests): drawn in the same place beside the time\n * as non-interactive text named by `label`, without the pill surface, so it does not look or act like a control.\n * Default `false` (the pill button that opens the hold menu).\n */\n readOnly?: boolean;\n}>;\n/**\n * Validates ChatMessage `reactions.summary` and marks which counts are drawn. The label (the button's accessible name)\n * is required; ids are unique and non-empty, emoji non-empty and counts positive integers; a count below\n * `reactionSummary.minCount` shows the emoji alone.\n */\nexport function resolveChatReactionSummary({ label, items }: ChatReactionSummary): readonly (ChatReactionSummaryItem & Readonly<{ showCount: boolean }>)[] {\n if (!label.trim()) throw new TypeError(\"Reaction summary requires a localized label\");\n const ids = new Set<string>();\n return items.map(item => {\n if (!item.id.trim() || ids.has(item.id)) throw new TypeError(\"Reaction summary ids must be unique and non-empty\");\n if (!item.emoji.trim()) throw new TypeError(\"Reaction summary items require an emoji\");\n if (!Number.isSafeInteger(item.count) || item.count < 1) throw new RangeError(\"Reaction summary counts must be positive integers\");\n ids.add(item.id);\n return { ...item, showCount: item.count >= chatMessageRecipe.reactionSummary.minCount };\n });\n}\n\n/**\n * \"Someone is typing\" row in a chat timeline (2026-10-18 BurnTok DM). It reuses the incoming bubble geometry\n * (chatMessageRecipe) so it reads as the next incoming message; only the dots are its own values. Under reduced\n * motion the dots stay at full opacity instead of pulsing.\n */\nexport const typingIndicatorRecipe = {\n dotSize: 6,\n dotGap: spacing.xxs,\n /** One pulse of the three dots; each dot starts `staggerMs` after the previous one. */\n cycleMs: 1200,\n staggerMs: 160,\n restOpacity: 0.35,\n} as const;\n\n/**\n * Centered date (or time) row between timeline groups (2026-10-18 Utilverse chat, which drew it as a product\n * caption). The next message still owns its own group gap above it, so this row only adds the space before it.\n */\nexport const chatDateSeparatorRecipe = {\n paddingTop: spacing.sm,\n paddingBottom: spacing.xxs,\n textVariant: \"caption\",\n tone: \"muted\",\n} as const;\n\n/** Column count for a photo set: pairs read as 2-up, other counts as 3-up; one photo spans the width. */\nexport function resolveChatMediaColumns(count: number): 1 | 2 | 3 {\n if (!Number.isSafeInteger(count) || count < 1) throw new RangeError(\"Chat media requires at least one item\");\n return count === 1 ? 1 : count === 2 || count === 4 ? 2 : 3;\n}\n\n/**\n * Paged photo album for ChatMessage `mediaAlbum` and CommentThreadItem `mediaAlbum` (2026-10-18, Utilverse social\n * photos). Only the shown page is rendered, so a product that authorizes each image can fetch one photo at a time\n * instead of every photo in a timeline row. The counter row matches CarouselMotion `navigation=\"counter\"`:\n * previous icon · position · next icon under the page; the frame keeps the host's media width and radius.\n */\nexport const mediaAlbumRecipe = {\n /** Page frame width / height when the product gives no aspect ratio. */\n defaultAspectRatio: 1,\n /** Space between the page frame and the counter row. */\n controlGap: spacing.xxs,\n /** Previous/next are small ghost IconButtons so the counter row stays compact beside thumbnails. */\n controlSize: \"small\",\n controlGlyphSize: glyph.sm,\n /**\n * Loading / failed page drawn by `MediaAlbumPageStatus` (2026-10-18 Utilverse: the page frame was one accessible\n * image, so the loading text and retry button inside it were unreachable by screen readers). The status block is\n * centered in the frame (VQ-09); retry is a small secondary Button.\n */\n pageStatus: { gap: spacing.xs, padding: spacing.sm, textVariant: \"caption\", retryTone: \"secondary\", retrySize: \"small\" },\n} as const;\n\nexport type MediaAlbumPageStatusKind = \"loading\" | \"error\";\n/** A page status names what the reader is waiting for or what failed; the label is the only copy. */\nexport function validateMediaAlbumPageStatus(kind: MediaAlbumPageStatusKind, label: string): void {\n if (kind !== \"loading\" && kind !== \"error\") throw new TypeError(\"Media album page status must be loading or error\");\n if (!label.trim()) throw new TypeError(\"Media album page status requires a localized label\");\n}\n\n/** A controlled index outside the set is a contract error, like Carousel's unknown `currentKey`. */\nexport function validateMediaAlbum(count: number, index: number): void {\n if (!Number.isSafeInteger(count) || count < 1) throw new RangeError(\"Media album requires at least one page\");\n if (!Number.isSafeInteger(index) || index < 0 || index >= count) throw new RangeError(\"Media album index must point at an existing page\");\n}\n\n/** One message or comment carries one photo set: either the eager `media` previews or the paged `mediaAlbum`. */\nexport function validateMediaSetChoice(hasMedia: boolean, hasAlbum: boolean): void {\n if (hasMedia && hasAlbum) throw new TypeError(\"Pass either media or mediaAlbum, not both\");\n}\n\n/** Uncontrolled albums keep the nearest page when the product's photo set shrinks (moderation, deletion). */\nexport function resolveMediaAlbumIndex(count: number, index: number): number {\n if (!Number.isSafeInteger(count) || count < 1) throw new RangeError(\"Media album requires at least one page\");\n return Math.min(Math.max(0, Math.trunc(index)), count - 1);\n}\n\n/**\n * Long-press menu geometry measured from the Instagram iOS DM hold menu (2026-10-16 @3x capture):\n * full-width reaction strip ~60pt with ~30pt emoji, the pressed bubble itself as preview, and a ~200pt\n * action card aligned to the bubble's side with ~56pt rows. 12pt between the three blocks.\n */\n/** Emoji row above a comment composer (Instagram comment sheet, 2026-10-16): evenly spread 44pt targets. */\nexport const composerQuickInsertRecipe = { itemSize: 44, emojiSize: 26, gap: spacing.xs } as const;\n\nexport const holdMenuRecipe = {\n inset: 10,\n gap: spacing.sm,\n stripHeight: 60,\n stripItemSize: 48,\n emojiSize: 28,\n actionWidth: 200,\n actionRowHeight: 56,\n actionRadius: 18,\n iconSize: 24,\n} as const;\n\n/** Position inside a run of consecutive messages from one author within one displayed minute. */\nexport type ChatMessageGroupPosition = \"single\" | \"first\" | \"middle\" | \"last\";\n\n/**\n * Runs break when the author or the displayed timestamp changes, so each minute shows its time once\n * and a new minute re-introduces the author (KakaoTalk behavior). Products pass the same localized\n * timestamp string they render; HJM never parses dates.\n */\nexport function resolveChatMessageGroups(items: readonly Readonly<{ id: string; authorId: string; timestamp: string }>[]): ReadonlyMap<string, ChatMessageGroupPosition> {\n const positions = new Map<string, ChatMessageGroupPosition>();\n items.forEach((item, index) => {\n if (!item.id.trim() || positions.has(item.id)) throw new TypeError(\"Chat messages require unique, nonempty ids\");\n const same = (other: (typeof items)[number] | undefined) => !!other && other.authorId === item.authorId && other.timestamp === item.timestamp;\n const before = same(items[index - 1]), after = same(items[index + 1]);\n positions.set(item.id, before ? (after ? \"middle\" : \"last\") : after ? \"first\" : \"single\");\n });\n return positions;\n}\n\n/** What one bubble shows for its run position; both renderers read this instead of re-deriving rules. */\nexport function resolveChatMessageGroupDisplay(position: ChatMessageGroupPosition = \"single\") {\n if (![\"single\", \"first\", \"middle\", \"last\"].includes(position)) throw new TypeError(\"Unknown chat message group position\");\n const starts = position === \"single\" || position === \"first\";\n const ends = position === \"single\" || position === \"last\";\n return { showAuthor: starts, showAvatar: starts, showTail: starts, showTimestamp: ends, spacingBefore: starts ? chatMessageRecipe.groupGap : chatMessageRecipe.runGap } as const;\n}\n\n/** Bubble alignment expresses authorship, not delivery; delivery text always comes from the product receipt. */\nexport type ChatMessageDescriptor = Readonly<{\n direction: \"incoming\" | \"outgoing\";\n author: string;\n timestamp: string;\n deliveryLabel?: string;\n /** Inward drag reveals time (incoming right, outgoing left) while delivery state remains visible; default shows time beside the last bubble of a run. */\n timestampPresentation?: \"always\" | \"swipe\";\n /** Authorship tip on the run's first bubble. Default on since the 2026-10-16 timeline standard. */\n bubbleTail?: boolean;\n /** From resolveChatMessageGroups; omitted means a standalone message. */\n groupPosition?: ChatMessageGroupPosition;\n}>;\n\n/** A denied permission goes to settings; rendering never requests OS access automatically. */\nexport type PermissionScreenStatus = \"prompt\" | \"denied\" | \"granted\" | \"unavailable\";\nexport function resolvePermissionAction(status:PermissionScreenStatus) {\n switch(status){case \"prompt\":return \"request\";case \"denied\":return \"settings\";case \"granted\":return \"continue\";case \"unavailable\":return null;default:throw new TypeError(\"Unknown permission state\");}\n}\nexport function resolveOnboardingStep(total:number,index:number){\n if(!Number.isInteger(total)||total<1||!Number.isInteger(index)||index<0||index>=total)throw new RangeError(\"Onboarding requires a valid step index\");\n return {first:index===0,last:index===total-1};\n}\n\n/**\n * Shared comment-row geometry. 2026-10-16 user decision: comments follow the measured Instagram iOS\n * comment sheet (@3x capture: avatar 32 / reply 24, avatar gap 8, like count under the heart,\n * \"Reply\" as quiet text below the body, replies indented to the text column). Artwork is not copied.\n */\nexport const commentThreadRecipe = {\n avatarSize: 32,\n replyAvatarSize: 24,\n avatarGap: spacing.xs,\n lineGap: 2,\n rowGap: spacing.sm,\n // Replies align with the parent's text column instead of an arbitrary section inset.\n replyIndent: 32 + spacing.xs,\n // The heart keeps the 44pt touch target even though the measured reference draws a smaller glyph.\n likeColumnWidth: 44,\n // Optional photo/sticker under the body; the measured reference keeps it thumbnail-sized.\n mediaMaxWidth: 160,\n mediaRadius: radius.md,\n // Inline per-comment panel (edit/report/block form) under the action row (2026-10-18, Utilverse): a form reads as its\n // own block, so it sits a full spacing step below the reply line instead of the 2pt line gap.\n panelGap: spacing.xs,\n // The trailing vertical-dots menu glyph; the button keeps the shared 44pt IconButton target.\n menuGlyphSize: glyph.sm,\n /**\n * Trailing actions (like, product actions, more) centre their 44 targets on the identity's first line instead of\n * top-aligning the target box (2026-10-17 measured audit: avatar 168 / author line 162 / glyphs 174). The cluster\n * shifts by (first-line height − visible target) / 2 — negative while the line is shorter than the target — and the\n * last control takes IconButton `edge=\"end\"` so its glyph slot ends on the row inset like the avatar starts on it.\n * The first line is the author caption, or the body line when the author runs inline with `bodyText`.\n */\n actionAnchor: { authorTextVariant: \"caption\", inlineTextVariant: \"body\" },\n} as const;\n\n/**\n * Items of the HJM-owned per-comment \"more\" menu (2026-10-17: message/comment structure belongs to HJM, review S4#7).\n * Same vocabulary as Menu/ContextMenu; the product supplies only ids, localized labels, tone and availability.\n */\nexport type CommentMenuItemDescriptor<Key extends string = string> = Pick<MenuItemDescriptor<Key>, \"id\" | \"label\" | \"disabled\" | \"tone\">;\n/** Danger actions (delete, block) always sit last so a destructive row is never the first target; order is otherwise kept. */\nexport function resolveCommentMenuItems<Key extends string>(items: readonly CommentMenuItemDescriptor<Key>[]): readonly CommentMenuItemDescriptor<Key>[] {\n const ids = new Set<string>();\n for (const item of items) {\n if (!item.id.trim() || ids.has(item.id) || !item.label.trim()) throw new TypeError(\"Comment menu items require unique ids and localized labels\");\n ids.add(item.id);\n }\n return [...items.filter(item => item.tone !== \"danger\"), ...items.filter(item => item.tone === \"danger\")];\n}\n\n/** Replies are one level deep; reject orphaned/duplicate records instead of silently losing them. */\nexport function validateCommentThread(items:readonly {id:string;parentId:string|null}[]){\n const roots=new Set(items.filter(item=>item.parentId===null).map(item=>item.id));const ids=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id))throw new TypeError(\"Comment ids must be unique and nonempty\");ids.add(item.id);if(item.parentId!==null&&(!roots.has(item.parentId)||item.parentId===item.id))throw new TypeError(\"Replies require an existing root comment\");}\n}\n\n/** Source bytes, permission requests, ordering and upload limits remain product-owned. */\nexport type MessageAttachmentDescriptor = Readonly<{ id: string; removeLabel: string; /** Local preparation can lock removal without locking text entry. */ disabled?: boolean }>;\nexport function validateMessageAttachments(attachments: readonly MessageAttachmentDescriptor[]): void {\n const ids = new Set<string>();\n for (const item of attachments) {\n if (!item.id.trim() || ids.has(item.id) || !item.removeLabel.trim()) throw new TypeError(\"Attachments require unique ids and localized removal labels\");\n ids.add(item.id);\n }\n}\n\n/** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */\nexport function isReplySwipe(dx: number, dy: number): boolean {\n return Math.abs(dx) >= screenPatternRecipe.replySwipeDistance && Math.abs(dx) > Math.abs(dy) * 2;\n}\n\n/** The UI never infers permission or camera availability; product adapters own both. */\nexport type PhotoSource = \"library\" | \"camera\";\nexport type PhotoSourceLabels = Readonly<{ title:string; library:string; camera:string; cancel:string }>;\n\nexport type SavedItem = Readonly<{ id: string; title: string }>;\nexport type SavedCollection = Readonly<{ id: string; title: string; itemIds: readonly string[] }>;\nexport type SavedItemsLabels = Readonly<{ allItems: string; privateNotice: string; back: string; empty: string; createCollection: string }>;\n\n/** Unsave removes an item from every collection view without rewriting product-owned membership. */\nexport function resolveSavedItems<T extends SavedItem>(items:readonly T[],collections:readonly SavedCollection[],collectionId:string|null|undefined,selectedItemId:string|null|undefined){\n const ids=new Set<string>(),groups=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id)||!item.title.trim())throw new TypeError(\"Saved items require unique ids and nonempty titles\");ids.add(item.id);}\n for(const group of collections){if(!group.id.trim()||groups.has(group.id)||!group.title.trim())throw new TypeError(\"Saved collections require unique ids and nonempty titles\");groups.add(group.id);}\n const collection=collections.find(item=>item.id===collectionId);\n // A deleted collection returns to the overview instead of showing a mislabeled all-posts view.\n const home=collectionId===undefined||(collectionId!==null&&!collection);\n const visible=home||collectionId===null?items:items.filter(item=>collection?.itemIds.includes(item.id));\n return {home,collection,visible,selected:home?undefined:visible.find(item=>item.id===selectedItemId)};\n}\n\n/**\n * SearchScreen phases (2026-10-06 search redesign, docs/screen-patterns.md#searchscreen-두-단계-검색).\n * Without a committed query (one-step search, the pre-1.13 behavior) any nonblank query is already\n * `results`. With one, typing a different query is `typing`: suggestions replace the results until the\n * user commits (Enter, a suggestion, a recent or suggested query). Comparing trimmed values keeps a\n * trailing space from flipping committed results back into suggestions.\n */\nexport type SearchScreenPhase = \"idle\" | \"typing\" | \"results\";\nexport function resolveSearchScreenPhase(query: string, committedQuery?: string): SearchScreenPhase {\n const typed = query.trim();\n if (!typed) return \"idle\";\n if (committedQuery === undefined) return \"results\";\n return typed === committedQuery.trim() ? \"results\" : \"typing\";\n}\n\n/**\n * The value a commit records, or `null` when there is nothing to commit. Blank commits are dropped\n * so a stray Enter never writes an empty recent search or fetches \"everything\".\n */\nexport function resolveSearchCommit(value: string): string | null {\n const next = value.trim();\n return next ? next : null;\n}\n\n/**\n * Why a committed search shows nothing. `filters` keeps the filter rail and offers \"clear all\";\n * `query` hides the rail (no filter can fix it) and offers suggested queries instead. Reference\n * services hide the rail on every zero result, which removes the only way to undo a filter.\n * `null` count means the result is still loading, so no cause is reported yet.\n */\nexport type SearchEmptyCause = \"none\" | \"filters\" | \"query\";\nexport function resolveSearchEmptyCause(count: number | null, appliedFilterCount: number): SearchEmptyCause {\n // Loading/nonempty results must not bypass the same count contract used by empty results.\n if ((count !== null && (count < 0 || !Number.isSafeInteger(count))) || !Number.isSafeInteger(appliedFilterCount) || appliedFilterCount < 0) {\n throw new RangeError(\"Search counts must be nonnegative integers\");\n }\n if (count === null || count > 0) return \"none\";\n return appliedFilterCount > 0 ? \"filters\" : \"query\";\n}\n\n/**\n * What the SearchScreen filter rail does to a result. `narrow` (default) only removes results, so with nothing applied\n * a zero result is the query's and no filter can bring results back. `scope` chooses what is searched (keyword vs\n * address, region, map area): changing it can turn a zero into results even with nothing applied (2026-10-18:\n * choose-window's keyword/address switch is the recovery for a zero result, so it skipped resultSummary entirely).\n */\nexport type SearchFiltersRole = \"narrow\" | \"scope\";\nexport const searchFiltersRoles = [\"narrow\", \"scope\"] as const satisfies readonly SearchFiltersRole[];\n/**\n * Whether SearchScreen shows its filter rail (`filters` and the filter sheet trigger). Two-step search shows it only\n * beside results so typing keeps the pinned area short. A query-caused zero hides it only when the filters narrow,\n * because then none of them can fix the zero; scope filters stay as the recovery path. A filter-caused zero always keeps\n * it so the cause can be undone. Both renderers call this so the rule cannot drift between platforms.\n */\nexport function resolveSearchFilterRailVisible(phase: SearchScreenPhase, twoStep: boolean, cause: SearchEmptyCause, role: SearchFiltersRole = \"narrow\"): boolean {\n if (!searchFiltersRoles.includes(role)) throw new RangeError(\"SearchScreen filtersRole must be narrow or scope\");\n if (twoStep && phase !== \"results\") return false;\n return cause !== \"query\" || role === \"scope\";\n}\n\n/**\n * Index to focus after removing item `removedIndex` from a list that now has `remaining` items:\n * the item that slid into its place, else the new last item, else -1 (move to the fallback target).\n * Removing a focused chip or row would otherwise drop focus to <body>.\n */\nexport function resolveFocusAfterRemoval(removedIndex: number, remaining: number): number {\n if (!Number.isInteger(removedIndex) || removedIndex < 0 || !Number.isInteger(remaining) || remaining < 0) {\n throw new RangeError(\"Removal indexes must be nonnegative integers\");\n }\n return remaining === 0 ? -1 : Math.min(removedIndex, remaining - 1);\n}\n\n/** Recipe for the SearchScreen phase sections; geometry stays on shared spacing tokens. */\nexport const searchScreenRecipe = {\n /** Debounced typing delay; explicit submit remains a separate host action. */\n debounceMs: 300,\n /** Recent searches shown before the user types; reference services show 5–10 and let the rest scroll away. */\n recentVisible: 5,\n /** Suggestions under the \"search for ‘q’\" row. */\n suggestionVisible: 6,\n /** Skeleton rows while the first page of results loads. */\n loadingRows: 4,\n /** Between idle sections (recent, suggested). */\n sectionGap: spacing.xl,\n /** Section title to its list, and results header to applied filters. */\n headerGap: spacing.sm,\n /** Between chips in applied filters and suggested queries. */\n chipGap: spacing.xs,\n} as const;\n\n/** Keep finite negative delays at zero; reject values whose timer behavior varies by host. */\nexport function resolveSearchDebounceMs(value: number = searchScreenRecipe.debounceMs): number {\n if (!Number.isFinite(value)) throw new RangeError(\"Search debounceMs must be finite\");\n return Math.max(0, value);\n}\n\n/** Horizontal intent preserves vertical scrolling and uses one reveal distance across renderers. */\nexport function timestampRevealOffset(dx: number, dy: number): number {\n // A 12px dead zone and 2:1 direction bias avoid turning taps/list scrolling into a time gesture.\n return dx > 12 && dx > Math.abs(dy) * 2 ? Math.min(dx, 88) : 0;\n}\n\n/** Notification previews need an actor/type badge and a separate subject thumbnail.\n * Keep this geometry shared so inboxes do not recreate the same row per product. */\nexport const notificationRowRecipe = {\n gap: 12, contentGap: 4, avatarSize: 44, avatarBadgeSize: 20,\n thumbnailSize: 44, unreadSize: 6, previewLines: 2, paddingBlock: 12, paddingInline: 16,\n} as const;\n\n/** Shared profile identity geometry; product owns the avatar content. */\nexport const profileSummaryRecipe = Object.freeze({\n avatarSize: 72,\n /**\n * ProfileScreen `editPlacement=\"summary\"` draws the edit action first in the summary's action row. A framed small\n * button reads as the card's own action; the below-summary placement keeps its ghost button (2026-10-15 utilverse\n * account decision: the edit action belongs inside the profile card).\n */\n editAction: Object.freeze({ tone: \"secondary\", size: \"small\" } as const),\n});\n\n/** Launcher artwork stays above its caption; product overlays may extend beyond the icon frame. */\n/**\n * Frame of an IconTile icon (2026-10-18 Utilverse launcher). `surface` draws the shared filled, hairlined frame;\n * `none` leaves product artwork that is already a complete launcher icon unframed. `shape` follows the product's\n * icon theme (squircle on iPhone shells, square on Windows/Linux shells); `size: \"fill\"` lets the icon span its\n * grid cell between `fillMinSize` and `fillMaxSize` instead of a product computing the cell size.\n */\nexport type IconTileFrame = \"surface\" | \"none\";\nexport type IconTileShape = \"squircle\" | \"square\" | \"circle\";\nexport type IconTileSize = \"fixed\" | \"fill\";\nexport const iconTileRecipe = Object.freeze({ iconSize: 64, labelMaxWidth: 72, multilineLabelMaxWidth: 84, multilineLabelMinHeight: 44, ghostOpacity: 0.55, gap: 6, iconRadius: \"22%\",\n /** Corner radius as a fraction of the icon size; `squircle` keeps the original 22%. */\n shapes: Object.freeze({ squircle: 0.22, square: 0, circle: 0.5 }) as Readonly<Record<IconTileShape, number>>,\n fillMinSize: 48, fillMaxSize: 68,\n defaults: Object.freeze({ frame: \"surface\" as IconTileFrame, shape: \"squircle\" as IconTileShape, size: \"fixed\" as IconTileSize }) });\n\n/**\n * What an IconTile passes to an `icon` render function: the rendered icon edge (`iconSize` for `fixed`, the measured\n * cell-clamped edge for `fill`). Fixed-size artwork (images, generated launcher art) sizes itself from it; Utilverse\n * re-computed the 48–68 clamp from its grid width to size its artwork (2026-10-18).\n */\nexport type IconTileArtwork = Readonly<{ edge: number }>;\n\n/** The edge a `size=\"fill\"` icon takes in a cell of `cellWidth`, for layout estimates made before the tile measures. */\nexport function resolveIconTileFillEdge(cellWidth: number): number {\n if (!Number.isFinite(cellWidth) || cellWidth < 0) throw new RangeError(\"IconTile cell width must be a non-negative finite number\");\n return Math.min(iconTileRecipe.fillMaxSize, Math.max(iconTileRecipe.fillMinSize, cellWidth));\n}\n"]}
1
+ {"version":3,"file":"screen-patterns.js","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { control, glyph, layout, opacity, radius, spacing } from \"./foundations.js\";\nimport { avatarRecipe, emptyStateRecipe } from \"./component-recipes.js\";\nimport type { MenuItemDescriptor } from \"./behaviors.js\";\n\n/** Initial load replaces the body; refresh/save errors belong beside retained content. */\nexport type ScreenContentState =\n | Readonly<{ kind: \"ready\" }>\n | Readonly<{ kind: \"loading\" | \"empty\" | \"error\" | \"restricted\"; title: string; description?: string }>;\n\nexport function resolveScreenContentState(state: ScreenContentState = { kind: \"ready\" }) {\n if (![\"ready\", \"loading\", \"empty\", \"error\", \"restricted\"].includes(state.kind)) {\n throw new TypeError(\"Unknown screen content state\");\n }\n if (state.kind !== \"ready\" && !state.title.trim()) {\n throw new TypeError(\"Screen state requires a localized title\");\n }\n return {\n kind: state.kind,\n replacesContent: state.kind !== \"ready\",\n busy: state.kind === \"loading\",\n // 2026-10-18: empty copy stays at the 14pt body size, including full-screen compositions.\n titleVariant: state.kind === \"empty\" ? emptyStateRecipe.title.textVariant : \"title\",\n announcement: state.kind === \"error\" ? \"assertive\" : \"polite\",\n } as const;\n}\n\n/**\n * Native header band heights. Titled centered headers match the iOS navigation bar (44 control + 4 + 4);\n * icon-only centered headers are compact (2026-10-14 야잘알 tab spacing, 2026-10-17 \"상단 여백 줄여\").\n * Body top padding shrinks only under a compact header without a notice.\n */\nexport const screenHeaderRecipe = {\n minHeight: 52,\n paddingVertical: spacing.xxs,\n compactMinHeight: 48,\n compactPaddingVertical: 2,\n compactBodyTop: spacing.xxs,\n /**\n * Heading levels for the route title, shared by Web and Native (2026-10-17 comparison: Native drew the\n * title as regular-weight Text — 20 start / 18 centered — while Web used bold Heading levels).\n */\n titleLevel: \"level3\",\n centeredTitleLevel: \"level5\",\n} as const;\n\n/**\n * Leaving a screen (back, close, \"설정 나가기\") is an icon-only button fixed at the header's leading edge\n * (2026-10-17 user rule: exits had drifted into body text buttons with mixed glyphs). The label is read\n * by assistive tech only. `back` mirrors in RTL; `close` is for modal-style routes.\n */\nexport type ScreenExitKind = \"back\" | \"close\";\nexport const screenExitRecipe = {\n /** Chevron arm length and stroke; close cross arm length. Drawn, not font glyphs, so every platform matches. */\n chevronSize: 10,\n closeSize: 14,\n stroke: 2,\n} as const;\nexport function validateScreenExit(exit: Readonly<{ kind: string; label: string }>, hasLeading: boolean) {\n if (exit.kind !== \"back\" && exit.kind !== \"close\") throw new TypeError(\"Screen exit kind must be back or close\");\n if (!exit.label.trim()) throw new TypeError(\"Screen exit needs a localized accessible label\");\n if (hasLeading) throw new TypeError(\"Screen exit owns the leading slot; do not also pass leading\");\n}\n\n/**\n * Who owns the screen gutters. `default`: the shared shell pads header, notice, body and footer. `none`: the host\n * route already insets everything (≈45 product call sites rely on this meaning, so it cannot change).\n * `body-edge`: header, notice and footer keep the shell padding; only the body's inline padding is 0 so full-width\n * list rows (ConversationRow, ListRow-based items) supply their own inset — review S5#9 measured DM avatars ~32pt\n * from the edge because body padding and row padding stacked (2026-10-17 user decision).\n * `body-bleed`: header, notice and footer keep the shell padding; the body has no gutter on any side, so a map or\n * canvas meets the header (or notice), the screen edges and the footer. With no footer the body is still the last\n * region and clears the bottom insets the screen owns (`safeAreaBottom`, an overlay BottomNavigation) without the\n * gutter. choose-window's map screens used `body-edge` and kept a 16pt strip above and below the map (2026-10-18).\n */\nexport type ScreenContentInset = \"default\" | \"none\" | \"body-edge\" | \"body-bleed\";\nexport const screenContentInsets = [\"default\", \"none\", \"body-edge\", \"body-bleed\"] as const satisfies readonly ScreenContentInset[];\n\n/**\n * 2026-10-18 사용자 지시: 입력 자동 포커스는 페이지 진입에만 적용하고 하단 시트에서는 폐지한다.\n * 페이지는 `first-input`으로 명시한 주 입력 또는 첫 유효 입력을 한 번 고른다. 하단 시트는 renderer의\n * 상속 경계에서 중첩 화면까지 자동 포커스를 막는다. Dialog·측면 시트의 모달 포커스 계약은 유지한다.\n * `single-input`은 Dialog·측면 시트의 단일 입력 선택, `none`은 입력 자동 선택을 끈다.\n * 비활성·읽기 전용·initialFocusTarget={false} 칸은 제외한다. 이미 포커스된 입력을 빼앗지 않으며\n * 화면은 mount당 한 번만 포커스한다. 모션 감소는 포커스 정책을 바꾸지 않는다.\n */\nexport type InitialFocusMode = \"first-input\" | \"single-input\" | \"none\";\nexport type ScreenInitialFocus = \"first-input\" | \"none\";\nexport type InitialFocusCandidate = Readonly<{\n /** Registration order inside the scope (tree order). */\n order: number;\n /** `initialFocusTarget` on the field: true prefers it, false excludes it, undefined keeps it eligible. */\n preference: boolean | undefined;\n /** False for disabled or read-only fields at resolve time. */\n focusable: boolean;\n}>;\n\n/** Defaults per composition; renderers read these instead of repeating literals. */\nexport const initialFocusDefaults = {\n // 2026-10-18: input focus belongs to page entry, including chat/auth pages; bottom sheets opt out at their boundary.\n screen: \"first-input\",\n search: \"first-input\",\n editor: \"first-input\",\n chat: \"first-input\",\n commentThread: \"first-input\",\n overlay: \"single-input\",\n auth: \"first-input\",\n} as const satisfies Record<string, InitialFocusMode>;\n\nexport function resolveInitialFocusTarget<T extends InitialFocusCandidate>(candidates: readonly T[], mode: InitialFocusMode): T | null {\n if (mode === \"none\") return null;\n const ordered = [...candidates].sort((a, b) => a.order - b.order);\n if (mode === \"single-input\") {\n const only = ordered.length === 1 ? ordered[0]! : null;\n return only && only.focusable && only.preference !== false ? only : null;\n }\n const eligible = ordered.filter(candidate => candidate.focusable && candidate.preference !== false);\n return eligible.find(candidate => candidate.preference === true) ?? eligible[0] ?? null;\n}\n\n/**\n * 진입 포커스 창(2026-10-18 1.28.1 앱 채택 점검). mount 시점 한 번만 고르면 세 경우가 앱 우회로 남았다: 진입 때 열린\n * 모달 아래 칸(번뚝 첫 가입 약관), 진입 중 비동기로 나타나는 칸(번뚝 계정 삭제 확인), 사용자 행동으로 나중에 나타나는 칸\n * (utilverse 검색 열기, 번뚝 아이디어 시트·spint 작성 단계). 그래서 범위는 아래 상태를 가진다.\n *\n * - `entering`: 진입 중(Native 전환·시트 등장 interaction 전). 칸 등록만 받는다.\n * - `open`: 진입이 끝났지만 아직 포커스하지 못했다. 칸이 새로 등록되거나 위 오버레이가 닫히면 다시 고른다. 모달 아래에서\n * 진입한 화면은 모달이 닫힐 때 사용자가 그 화면을 아직 만지지 않았으면 그때 포커스한다.\n * - `settled`: 포커스했거나, 사용자가 범위를 직접 조작(터치·포인터·키·휠)했거나, 이미 다른 텍스트 칸에 초점이 있다.\n * 이후에 입력이 새로 나타나도 자동 포커스를 다시 시작하지 않는다(2026-10-18 페이지 진입에만 적용).\n *\n * 위에 다른 오버레이가 열려 있는 동안(`blocked`)은 고르지 않고, 그 동안의 조작은 아래 범위의 조작으로 세지 않는다\n * (약관 동의 탭이 화면의 진입 포커스를 끝내면 안 된다). 텍스트 칸에 이미 초점이 있으면 빼앗지 않는다.\n */\nexport type EntryFocusPhase = \"inactive\" | \"entering\" | \"open\" | \"settled\";\nexport type EntryFocusEvent<T extends InitialFocusCandidate> =\n /** The scope mounted (screen) or opened (overlay). */\n | Readonly<{ kind: \"activate\" }>\n /** The overlay closed or the scope unmounted. */\n | Readonly<{ kind: \"deactivate\" }>\n /** The enter transition finished. */\n | Readonly<{ kind: \"enter\" }>\n /** A field mounted into the scope (first registration of that field instance). */\n | Readonly<{ kind: \"register\"; field: T }>\n /** An overlay above the scope closed. */\n | Readonly<{ kind: \"unblock\" }>\n /** User touch, pointer, key or wheel input reached the scope. */\n | Readonly<{ kind: \"interact\" }>;\nexport type EntryFocusFacts<T extends InitialFocusCandidate> = Readonly<{\n mode: InitialFocusMode;\n /** Every field registered in the scope now, including a newly registered one. */\n candidates: readonly T[];\n /** Another overlay is open above this scope. */\n blocked: boolean;\n /** A text field already has focus (the user's or the app's). */\n textEntryFocused: boolean;\n}>;\nexport type EntryFocusResolution<T> = Readonly<{ phase: EntryFocusPhase; target: T | null }>;\n\nexport function resolveEntryFocus<T extends InitialFocusCandidate>(phase: EntryFocusPhase, event: EntryFocusEvent<T>, facts: EntryFocusFacts<T>): EntryFocusResolution<T> {\n const stay = { phase, target: null };\n if (event.kind === \"deactivate\") return { phase: \"inactive\", target: null };\n if (event.kind === \"activate\") return { phase: \"entering\", target: null };\n if (phase === \"inactive\") return stay;\n if (event.kind === \"interact\") return facts.blocked || phase === \"settled\" ? stay : { phase: \"settled\", target: null };\n const attempt = (): EntryFocusResolution<T> => {\n if (facts.blocked) return { phase: \"open\", target: null };\n if (facts.textEntryFocused) return { phase: \"settled\", target: null };\n const target = resolveInitialFocusTarget(facts.candidates, facts.mode);\n return target ? { phase: \"settled\", target } : { phase: \"open\", target: null };\n };\n if (event.kind === \"enter\") return phase !== \"entering\" ? stay : facts.mode === \"none\" ? { phase: \"settled\", target: null } : attempt();\n if (phase === \"entering\") return stay;\n if (phase === \"open\") return attempt();\n // Page entry only: revealing a marked field after interaction/entry must not open the keyboard again.\n return stay;\n}\n\n// Reuse reading-width and spacing tokens; do not derive geometry from a product's device or artwork.\nexport const screenPatternRecipe = {\n // Full-screen reading routes use fixed travel, unlike a Sheet's fraction of its own height.\n // Migrated from Diairy's handle/top-bounce contract so products no longer own dismissal physics.\n pullDismiss: {\n activationDistance: 6,\n distance: 80,\n flickDistance: 32,\n flickVelocity: 0.65,\n maxOffset: 160,\n settleDuration: 140,\n topTolerance: 1,\n },\n maxWidth: layout.readingMaxWidth,\n padding: spacing.md,\n sectionGap: spacing.xl,\n itemGap: spacing.sm,\n stateGap: spacing.md,\n // Compact screens must keep back/title/save on one row; scale this floor for large text instead of reserving a desktop-width title.\n headerMinWidth: layout.readingMaxWidth / 6,\n // Reserve the opposite edge for authorship and cap the composer so history remains visible.\n messageMaxWidth: \"84%\",\n // Social thumbnails stay compact; selection order and removal replace a second toolbar.\n attachmentSize: 80,\n attachmentRemoveSize: 24,\n reactionHoldMs: 450,\n reactionMoveTolerance: 10,\n reactionMenuWidth: 320,\n replySwipeDistance: 60,\n composerMinLines: 1,\n composerMaxLines: 5,\n} as const;\n\n/** Controlled composer: HJM never clears drafts or interprets a fulfilled callback as server delivery. */\nexport type MessageComposerDescriptor = Readonly<{\n value: string;\n label: string;\n sendLabel: string;\n /** Short visual copy may differ from the complete accessible input name. */\n placeholder?: string;\n description?: string;\n /** Counters can align to the field end; validation errors retain their normal alignment. */\n descriptionAlign?: \"start\" | \"end\";\n error?: string;\n invalid?: boolean;\n /** Newline remains the compatible default; desktop chat may explicitly opt into send. */\n submitMode?: \"newline\" | \"send\";\n disabled?: boolean;\n pending?: boolean;\n attachmentCount?: number;\n}>;\n/** Safari may report the final IME Enter as keyCode 229 after isComposing becomes false. */\nexport function shouldSubmitMessageKey(event: Readonly<{ key: string; shiftKey?: boolean; isComposing?: boolean; keyCode?: number }>, mode: \"newline\" | \"send\" = \"newline\"): boolean {\n return mode === \"send\" && event.key === \"Enter\" && !event.shiftKey && !event.isComposing && event.keyCode !== 229;\n}\nexport function canSubmitMessage({ value, disabled = false, pending = false, attachmentCount = 0 }: MessageComposerDescriptor): boolean {\n if (!Number.isSafeInteger(attachmentCount) || attachmentCount < 0) throw new RangeError(\"Attachment count must be a nonnegative integer\");\n return !disabled && !pending && (value.trim().length > 0 || attachmentCount > 0);\n}\n\n/**\n * Shared timeline geometry. 2026-10-16 user decision: the common chat standard follows the measured\n * KakaoTalk iOS chat room (@3x capture: bubble ~35pt tall, radius ~14, run gap ~6, group gap ~12,\n * avatar ~34, avatar gap 8). Brand colors and artwork are not copied; surfaces stay semantic tokens.\n */\nexport const chatMessageRecipe = {\n // Must be a size the Avatar renders on both platforms: 36 was no Web Avatar step (32/40/48/64), so Web drew a 32\n // avatar in a 36 slot while Native drew 36 (2026-10-17 measured audit). `small` is the step nearest the ~34 reference.\n avatarSize: avatarRecipe.sizes.small,\n avatarGap: spacing.xs,\n authorGap: spacing.xxs,\n metaGap: spacing.xxs,\n bubbleRadius: 14,\n bubblePaddingInline: 10,\n bubblePaddingBlock: 6,\n // Bubbles of one run read as a stack; a new author or minute starts a visibly separate group.\n runGap: 6,\n groupGap: spacing.sm,\n // Flat-topped tip continuing the bubble's top edge; the tipped corner tightens to meet it.\n tailWidth: 7,\n tailHeight: 10,\n tailCornerRadius: 4,\n jumpToLatestGap: spacing.xs,\n // Timeline side gutter (measured 10pt); header and composer keep the screen padding.\n timelineInset: 10,\n // Photos render without the bubble surface (measured reference: ~210pt wide, bubble radius).\n mediaWidth: 210,\n mediaGap: 2,\n /**\n * Reaction summary pill beside the time (2026-10-18 BurnTok DM): one button that opens the same hold menu.\n * Counts below `minCount` show the emoji alone, as the measured DM references do for a single reaction.\n */\n reactionSummary: { minHeight: 24, paddingInline: spacing.xs, itemGap: spacing.xxs, emojiGap: 2, minCount: 2 },\n} as const;\n\nexport type ChatReactionSummaryItem = Readonly<{ id: string; emoji: string; count: number }>;\nexport type ChatReactionSummary = Readonly<{\n label: string;\n items: readonly ChatReactionSummaryItem[];\n /**\n * Counts only, for readers who cannot react (2026-10-18 Utilverse guests): drawn in the same place beside the time\n * as non-interactive text named by `label`, without the pill surface, so it does not look or act like a control.\n * Default `false` (the pill button that opens the hold menu).\n */\n readOnly?: boolean;\n}>;\n/**\n * Validates ChatMessage `reactions.summary` and marks which counts are drawn. The label (the button's accessible name)\n * is required; ids are unique and non-empty, emoji non-empty and counts positive integers; a count below\n * `reactionSummary.minCount` shows the emoji alone.\n */\nexport function resolveChatReactionSummary({ label, items }: ChatReactionSummary): readonly (ChatReactionSummaryItem & Readonly<{ showCount: boolean }>)[] {\n if (!label.trim()) throw new TypeError(\"Reaction summary requires a localized label\");\n const ids = new Set<string>();\n return items.map(item => {\n if (!item.id.trim() || ids.has(item.id)) throw new TypeError(\"Reaction summary ids must be unique and non-empty\");\n if (!item.emoji.trim()) throw new TypeError(\"Reaction summary items require an emoji\");\n if (!Number.isSafeInteger(item.count) || item.count < 1) throw new RangeError(\"Reaction summary counts must be positive integers\");\n ids.add(item.id);\n return { ...item, showCount: item.count >= chatMessageRecipe.reactionSummary.minCount };\n });\n}\n\n/**\n * \"Someone is typing\" row in a chat timeline (2026-10-18 BurnTok DM). It reuses the incoming bubble geometry\n * (chatMessageRecipe) so it reads as the next incoming message; only the dots are its own values. Under reduced\n * motion the dots stay at full opacity instead of pulsing.\n */\nexport const typingIndicatorRecipe = {\n dotSize: 6,\n dotGap: spacing.xxs,\n /** One pulse of the three dots; each dot starts `staggerMs` after the previous one. */\n cycleMs: 1200,\n staggerMs: 160,\n restOpacity: 0.35,\n} as const;\n\n/**\n * Centered date (or time) row between timeline groups (2026-10-18 Utilverse chat, which drew it as a product\n * caption). The next message still owns its own group gap above it, so this row only adds the space before it.\n */\nexport const chatDateSeparatorRecipe = {\n paddingTop: spacing.sm,\n paddingBottom: spacing.xxs,\n textVariant: \"caption\",\n tone: \"muted\",\n} as const;\n\n/** Column count for a photo set: pairs read as 2-up, other counts as 3-up; one photo spans the width. */\nexport function resolveChatMediaColumns(count: number): 1 | 2 | 3 {\n if (!Number.isSafeInteger(count) || count < 1) throw new RangeError(\"Chat media requires at least one item\");\n return count === 1 ? 1 : count === 2 || count === 4 ? 2 : 3;\n}\n\n/**\n * Paged photo album for ChatMessage `mediaAlbum` and CommentThreadItem `mediaAlbum` (2026-10-18, Utilverse social\n * photos). Only the shown page is rendered, so a product that authorizes each image can fetch one photo at a time\n * instead of every photo in a timeline row. The counter row matches CarouselMotion `navigation=\"counter\"`:\n * previous icon · position · next icon under the page; the frame keeps the host's media width and radius.\n */\nexport const mediaAlbumRecipe = {\n /** Page frame width / height when the product gives no aspect ratio. */\n defaultAspectRatio: 1,\n /** Space between the page frame and the counter row. */\n controlGap: spacing.xxs,\n /** Previous/next are small ghost IconButtons so the counter row stays compact beside thumbnails. */\n controlSize: \"small\",\n controlGlyphSize: glyph.sm,\n /**\n * Loading / failed page drawn by `MediaAlbumPageStatus` (2026-10-18 Utilverse: the page frame was one accessible\n * image, so the loading text and retry button inside it were unreachable by screen readers). The status block is\n * centered in the frame (VQ-09); retry is a small secondary Button.\n */\n pageStatus: { gap: spacing.xs, padding: spacing.sm, textVariant: \"caption\", retryTone: \"secondary\", retrySize: \"small\" },\n} as const;\n\nexport type MediaAlbumPageStatusKind = \"loading\" | \"error\";\n/** A page status names what the reader is waiting for or what failed; the label is the only copy. */\nexport function validateMediaAlbumPageStatus(kind: MediaAlbumPageStatusKind, label: string): void {\n if (kind !== \"loading\" && kind !== \"error\") throw new TypeError(\"Media album page status must be loading or error\");\n if (!label.trim()) throw new TypeError(\"Media album page status requires a localized label\");\n}\n\n/** A controlled index outside the set is a contract error, like Carousel's unknown `currentKey`. */\nexport function validateMediaAlbum(count: number, index: number): void {\n if (!Number.isSafeInteger(count) || count < 1) throw new RangeError(\"Media album requires at least one page\");\n if (!Number.isSafeInteger(index) || index < 0 || index >= count) throw new RangeError(\"Media album index must point at an existing page\");\n}\n\n/** One message or comment carries one photo set: either the eager `media` previews or the paged `mediaAlbum`. */\nexport function validateMediaSetChoice(hasMedia: boolean, hasAlbum: boolean): void {\n if (hasMedia && hasAlbum) throw new TypeError(\"Pass either media or mediaAlbum, not both\");\n}\n\n/** Uncontrolled albums keep the nearest page when the product's photo set shrinks (moderation, deletion). */\nexport function resolveMediaAlbumIndex(count: number, index: number): number {\n if (!Number.isSafeInteger(count) || count < 1) throw new RangeError(\"Media album requires at least one page\");\n return Math.min(Math.max(0, Math.trunc(index)), count - 1);\n}\n\n/**\n * Long-press menu geometry measured from the Instagram iOS DM hold menu (2026-10-16 @3x capture):\n * full-width reaction strip ~60pt with ~30pt emoji, the pressed bubble itself as preview, and a ~200pt\n * action card aligned to the bubble's side with ~56pt rows. 12pt between the three blocks.\n */\n/** Emoji row above a comment composer (Instagram comment sheet, 2026-10-16): evenly spread 44pt targets. */\nexport const composerQuickInsertRecipe = { itemSize: 44, emojiSize: 26, gap: spacing.xs } as const;\n\nexport const holdMenuRecipe = {\n inset: 10,\n gap: spacing.sm,\n stripHeight: 60,\n stripItemSize: 48,\n emojiSize: 28,\n actionWidth: 200,\n actionRowHeight: 56,\n actionRadius: 18,\n iconSize: 24,\n} as const;\n\n/** Position inside a run of consecutive messages from one author within one displayed minute. */\nexport type ChatMessageGroupPosition = \"single\" | \"first\" | \"middle\" | \"last\";\n\n/**\n * Runs break when the author or the displayed timestamp changes, so each minute shows its time once\n * and a new minute re-introduces the author (KakaoTalk behavior). Products pass the same localized\n * timestamp string they render; HJM never parses dates.\n */\nexport function resolveChatMessageGroups(items: readonly Readonly<{ id: string; authorId: string; timestamp: string }>[]): ReadonlyMap<string, ChatMessageGroupPosition> {\n const positions = new Map<string, ChatMessageGroupPosition>();\n items.forEach((item, index) => {\n if (!item.id.trim() || positions.has(item.id)) throw new TypeError(\"Chat messages require unique, nonempty ids\");\n const same = (other: (typeof items)[number] | undefined) => !!other && other.authorId === item.authorId && other.timestamp === item.timestamp;\n const before = same(items[index - 1]), after = same(items[index + 1]);\n positions.set(item.id, before ? (after ? \"middle\" : \"last\") : after ? \"first\" : \"single\");\n });\n return positions;\n}\n\n/** What one bubble shows for its run position; both renderers read this instead of re-deriving rules. */\nexport function resolveChatMessageGroupDisplay(position: ChatMessageGroupPosition = \"single\") {\n if (![\"single\", \"first\", \"middle\", \"last\"].includes(position)) throw new TypeError(\"Unknown chat message group position\");\n const starts = position === \"single\" || position === \"first\";\n const ends = position === \"single\" || position === \"last\";\n return { showAuthor: starts, showAvatar: starts, showTail: starts, showTimestamp: ends, spacingBefore: starts ? chatMessageRecipe.groupGap : chatMessageRecipe.runGap } as const;\n}\n\n/** Bubble alignment expresses authorship, not delivery; delivery text always comes from the product receipt. */\nexport type ChatMessageDescriptor = Readonly<{\n direction: \"incoming\" | \"outgoing\";\n author: string;\n timestamp: string;\n deliveryLabel?: string;\n /** Inward drag reveals time (incoming right, outgoing left) while delivery state remains visible; default shows time beside the last bubble of a run. */\n timestampPresentation?: \"always\" | \"swipe\";\n /** Authorship tip on the run's first bubble. Default on since the 2026-10-16 timeline standard. */\n bubbleTail?: boolean;\n /** From resolveChatMessageGroups; omitted means a standalone message. */\n groupPosition?: ChatMessageGroupPosition;\n}>;\n\n/** A denied permission goes to settings; rendering never requests OS access automatically. */\nexport type PermissionScreenStatus = \"prompt\" | \"denied\" | \"granted\" | \"unavailable\";\nexport function resolvePermissionAction(status:PermissionScreenStatus) {\n switch(status){case \"prompt\":return \"request\";case \"denied\":return \"settings\";case \"granted\":return \"continue\";case \"unavailable\":return null;default:throw new TypeError(\"Unknown permission state\");}\n}\nexport function resolveOnboardingStep(total:number,index:number){\n if(!Number.isInteger(total)||total<1||!Number.isInteger(index)||index<0||index>=total)throw new RangeError(\"Onboarding requires a valid step index\");\n return {first:index===0,last:index===total-1};\n}\n\n/**\n * Shared comment-row geometry. 2026-10-16 user decision: comments follow the measured Instagram iOS\n * comment sheet (@3x capture: avatar 32 / reply 24, avatar gap 8, like count under the heart,\n * \"Reply\" as quiet text below the body, replies indented to the text column). Artwork is not copied.\n */\nexport const commentThreadRecipe = {\n avatarSize: 32,\n replyAvatarSize: 24,\n avatarGap: spacing.xs,\n lineGap: 2,\n rowGap: spacing.sm,\n // Replies align with the parent's text column instead of an arbitrary section inset.\n replyIndent: 32 + spacing.xs,\n // The heart keeps the 44pt touch target even though the measured reference draws a smaller glyph.\n likeColumnWidth: 44,\n // Optional photo/sticker under the body; the measured reference keeps it thumbnail-sized.\n mediaMaxWidth: 160,\n mediaRadius: radius.md,\n // Inline per-comment panel (edit/report/block form) under the action row (2026-10-18, Utilverse): a form reads as its\n // own block, so it sits a full spacing step below the reply line instead of the 2pt line gap.\n panelGap: spacing.xs,\n // The trailing vertical-dots menu glyph; the button keeps the shared 44pt IconButton target.\n menuGlyphSize: glyph.sm,\n /**\n * Trailing actions (like, product actions, more) centre their 44 targets on the identity's first line instead of\n * top-aligning the target box (2026-10-17 measured audit: avatar 168 / author line 162 / glyphs 174). The cluster\n * shifts by (first-line height − visible target) / 2 — negative while the line is shorter than the target — and the\n * last control takes IconButton `edge=\"end\"` so its glyph slot ends on the row inset like the avatar starts on it.\n * The first line is the author caption, or the body line when the author runs inline with `bodyText`.\n */\n actionAnchor: { authorTextVariant: \"caption\", inlineTextVariant: \"body\" },\n} as const;\n\n/**\n * Items of the HJM-owned per-comment \"more\" menu (2026-10-17: message/comment structure belongs to HJM, review S4#7).\n * Same vocabulary as Menu/ContextMenu; the product supplies only ids, localized labels, tone and availability.\n */\nexport type CommentMenuItemDescriptor<Key extends string = string> = Pick<MenuItemDescriptor<Key>, \"id\" | \"label\" | \"disabled\" | \"tone\">;\n/** Danger actions (delete, block) always sit last so a destructive row is never the first target; order is otherwise kept. */\nexport function resolveCommentMenuItems<Key extends string>(items: readonly CommentMenuItemDescriptor<Key>[]): readonly CommentMenuItemDescriptor<Key>[] {\n const ids = new Set<string>();\n for (const item of items) {\n if (!item.id.trim() || ids.has(item.id) || !item.label.trim()) throw new TypeError(\"Comment menu items require unique ids and localized labels\");\n ids.add(item.id);\n }\n return [...items.filter(item => item.tone !== \"danger\"), ...items.filter(item => item.tone === \"danger\")];\n}\n\n/** Replies are one level deep; reject orphaned/duplicate records instead of silently losing them. */\nexport function validateCommentThread(items:readonly {id:string;parentId:string|null}[]){\n const roots=new Set(items.filter(item=>item.parentId===null).map(item=>item.id));const ids=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id))throw new TypeError(\"Comment ids must be unique and nonempty\");ids.add(item.id);if(item.parentId!==null&&(!roots.has(item.parentId)||item.parentId===item.id))throw new TypeError(\"Replies require an existing root comment\");}\n}\n\n/** Source bytes, permission requests, ordering and upload limits remain product-owned. */\nexport type MessageAttachmentDescriptor = Readonly<{ id: string; removeLabel: string; /** Local preparation can lock removal without locking text entry. */ disabled?: boolean }>;\nexport function validateMessageAttachments(attachments: readonly MessageAttachmentDescriptor[]): void {\n const ids = new Set<string>();\n for (const item of attachments) {\n if (!item.id.trim() || ids.has(item.id) || !item.removeLabel.trim()) throw new TypeError(\"Attachments require unique ids and localized removal labels\");\n ids.add(item.id);\n }\n}\n\n/** Require horizontal intent so a timeline scroll cannot accidentally start a reply. */\nexport function isReplySwipe(dx: number, dy: number): boolean {\n return Math.abs(dx) >= screenPatternRecipe.replySwipeDistance && Math.abs(dx) > Math.abs(dy) * 2;\n}\n\n/** The UI never infers permission or camera availability; product adapters own both. */\nexport type PhotoSource = \"library\" | \"camera\";\nexport type PhotoSourceLabels = Readonly<{ title:string; library:string; camera:string; cancel:string }>;\n\nexport type SavedItem = Readonly<{ id: string; title: string }>;\nexport type SavedCollection = Readonly<{ id: string; title: string; itemIds: readonly string[] }>;\nexport type SavedItemsLabels = Readonly<{ allItems: string; privateNotice: string; back: string; empty: string; createCollection: string }>;\n\n/** Unsave removes an item from every collection view without rewriting product-owned membership. */\nexport function resolveSavedItems<T extends SavedItem>(items:readonly T[],collections:readonly SavedCollection[],collectionId:string|null|undefined,selectedItemId:string|null|undefined){\n const ids=new Set<string>(),groups=new Set<string>();\n for(const item of items){if(!item.id.trim()||ids.has(item.id)||!item.title.trim())throw new TypeError(\"Saved items require unique ids and nonempty titles\");ids.add(item.id);}\n for(const group of collections){if(!group.id.trim()||groups.has(group.id)||!group.title.trim())throw new TypeError(\"Saved collections require unique ids and nonempty titles\");groups.add(group.id);}\n const collection=collections.find(item=>item.id===collectionId);\n // A deleted collection returns to the overview instead of showing a mislabeled all-posts view.\n const home=collectionId===undefined||(collectionId!==null&&!collection);\n const visible=home||collectionId===null?items:items.filter(item=>collection?.itemIds.includes(item.id));\n return {home,collection,visible,selected:home?undefined:visible.find(item=>item.id===selectedItemId)};\n}\n\n/**\n * SearchScreen phases (2026-10-06 search redesign, docs/screen-patterns.md#searchscreen-두-단계-검색).\n * Without a committed query (one-step search, the pre-1.13 behavior) any nonblank query is already\n * `results`. With one, typing a different query is `typing`: suggestions replace the results until the\n * user commits (Enter, a suggestion, a recent or suggested query). Comparing trimmed values keeps a\n * trailing space from flipping committed results back into suggestions.\n */\nexport type SearchScreenPhase = \"idle\" | \"typing\" | \"results\";\nexport function resolveSearchScreenPhase(query: string, committedQuery?: string): SearchScreenPhase {\n const typed = query.trim();\n if (!typed) return \"idle\";\n if (committedQuery === undefined) return \"results\";\n return typed === committedQuery.trim() ? \"results\" : \"typing\";\n}\n\n/**\n * The value a commit records, or `null` when there is nothing to commit. Blank commits are dropped\n * so a stray Enter never writes an empty recent search or fetches \"everything\".\n */\nexport function resolveSearchCommit(value: string): string | null {\n const next = value.trim();\n return next ? next : null;\n}\n\n/**\n * Why a committed search shows nothing. `filters` keeps the filter rail and offers \"clear all\";\n * `query` hides the rail (no filter can fix it) and offers suggested queries instead. Reference\n * services hide the rail on every zero result, which removes the only way to undo a filter.\n * `null` count means the result is still loading, so no cause is reported yet.\n */\nexport type SearchEmptyCause = \"none\" | \"filters\" | \"query\";\nexport function resolveSearchEmptyCause(count: number | null, appliedFilterCount: number): SearchEmptyCause {\n // Loading/nonempty results must not bypass the same count contract used by empty results.\n if ((count !== null && (count < 0 || !Number.isSafeInteger(count))) || !Number.isSafeInteger(appliedFilterCount) || appliedFilterCount < 0) {\n throw new RangeError(\"Search counts must be nonnegative integers\");\n }\n if (count === null || count > 0) return \"none\";\n return appliedFilterCount > 0 ? \"filters\" : \"query\";\n}\n\n/**\n * What the SearchScreen filter rail does to a result. `narrow` (default) only removes results, so with nothing applied\n * a zero result is the query's and no filter can bring results back. `scope` chooses what is searched (keyword vs\n * address, region, map area): changing it can turn a zero into results even with nothing applied (2026-10-18:\n * choose-window's keyword/address switch is the recovery for a zero result, so it skipped resultSummary entirely).\n */\nexport type SearchFiltersRole = \"narrow\" | \"scope\";\nexport const searchFiltersRoles = [\"narrow\", \"scope\"] as const satisfies readonly SearchFiltersRole[];\n/**\n * Whether SearchScreen shows its filter rail (`filters` and the filter sheet trigger). Two-step search shows it only\n * beside results so typing keeps the pinned area short. A query-caused zero hides it only when the filters narrow,\n * because then none of them can fix the zero; scope filters stay as the recovery path. A filter-caused zero always keeps\n * it so the cause can be undone. Both renderers call this so the rule cannot drift between platforms.\n */\nexport function resolveSearchFilterRailVisible(phase: SearchScreenPhase, twoStep: boolean, cause: SearchEmptyCause, role: SearchFiltersRole = \"narrow\"): boolean {\n if (!searchFiltersRoles.includes(role)) throw new RangeError(\"SearchScreen filtersRole must be narrow or scope\");\n if (twoStep && phase !== \"results\") return false;\n return cause !== \"query\" || role === \"scope\";\n}\n\n/**\n * Index to focus after removing item `removedIndex` from a list that now has `remaining` items:\n * the item that slid into its place, else the new last item, else -1 (move to the fallback target).\n * Removing a focused chip or row would otherwise drop focus to <body>.\n */\nexport function resolveFocusAfterRemoval(removedIndex: number, remaining: number): number {\n if (!Number.isInteger(removedIndex) || removedIndex < 0 || !Number.isInteger(remaining) || remaining < 0) {\n throw new RangeError(\"Removal indexes must be nonnegative integers\");\n }\n return remaining === 0 ? -1 : Math.min(removedIndex, remaining - 1);\n}\n\n/** Recipe for the SearchScreen phase sections; geometry stays on shared spacing tokens. */\nexport const searchScreenRecipe = {\n /** Debounced typing delay; explicit submit remains a separate host action. */\n debounceMs: 300,\n /** Recent searches shown before the user types; reference services show 5–10 and let the rest scroll away. */\n recentVisible: 5,\n /** Suggestions under the \"search for ‘q’\" row. */\n suggestionVisible: 6,\n /** Skeleton rows while the first page of results loads. */\n loadingRows: 4,\n /** Between idle sections (recent, suggested). */\n sectionGap: spacing.xl,\n /** Section title to its list, and results header to applied filters. */\n headerGap: spacing.sm,\n /** Between chips in applied filters and suggested queries. */\n chipGap: spacing.xs,\n} as const;\n\n/** Keep finite negative delays at zero; reject values whose timer behavior varies by host. */\nexport function resolveSearchDebounceMs(value: number = searchScreenRecipe.debounceMs): number {\n if (!Number.isFinite(value)) throw new RangeError(\"Search debounceMs must be finite\");\n return Math.max(0, value);\n}\n\n/** Horizontal intent preserves vertical scrolling and uses one reveal distance across renderers. */\nexport function timestampRevealOffset(dx: number, dy: number): number {\n // A 12px dead zone and 2:1 direction bias avoid turning taps/list scrolling into a time gesture.\n return dx > 12 && dx > Math.abs(dy) * 2 ? Math.min(dx, 88) : 0;\n}\n\n/** Notification previews need an actor/type badge and a separate subject thumbnail.\n * Keep this geometry shared so inboxes do not recreate the same row per product. */\nconst notificationRowGeometry = {\n gap: 12, contentGap: 4, avatarSize: 44, avatarBadgeSize: 20,\n thumbnailSize: 44, unreadSize: 6, previewLines: 2, paddingBlock: 12, paddingInline: 16,\n} as const;\nexport const notificationRowRecipe = {\n ...notificationRowGeometry,\n // Virtualized inboxes provide the section boundary; the shared row owns the text-aligned separator.\n separatorInsetStart: notificationRowGeometry.paddingInline + notificationRowGeometry.avatarSize + notificationRowGeometry.gap,\n interaction: { disabledOpacity: opacity.disabled, pressedOpacity: opacity.pressed },\n} as const;\n\n/** Shared profile identity geometry; product owns the avatar content. */\nexport const profileSummaryRecipe = Object.freeze({\n avatarSize: 72,\n avatarGlyphSize: glyph.xxl,\n actionsLayouts: Object.freeze({\n inline: Object.freeze({ axis: \"inline\", gap: \"sm\" } as const),\n stacked: Object.freeze({ axis: \"block\", gap: \"xs\" } as const),\n }),\n /**\n * ProfileScreen `editPlacement=\"summary\"` draws the edit action first in the summary's action row. A framed small\n * button reads as the card's own action; the below-summary placement keeps its ghost button (2026-10-15 utilverse\n * account decision: the edit action belongs inside the profile card).\n */\n editAction: Object.freeze({ tone: \"secondary\", size: \"small\" } as const),\n});\n\n/** Launcher artwork stays above its caption; product overlays may extend beyond the icon frame. */\n/**\n * Frame of an IconTile icon (2026-10-18 Utilverse launcher). `surface` draws the shared filled, hairlined frame;\n * `none` leaves product artwork that is already a complete launcher icon unframed. `shape` follows the product's\n * icon theme (squircle on iPhone shells, square on Windows/Linux shells); `size: \"fill\"` lets the icon span its\n * grid cell between `fillMinSize` and `fillMaxSize` instead of a product computing the cell size.\n */\nexport type IconTileFrame = \"surface\" | \"none\";\nexport type IconTileShape = \"squircle\" | \"square\" | \"circle\";\nexport type IconTileSize = \"fixed\" | \"fill\";\nexport const iconTileRecipe = Object.freeze({ iconSize: 64, labelMaxWidth: 72, multilineLabelMaxWidth: 84, multilineLabelMinHeight: 44, ghostOpacity: 0.55, gap: 6, iconRadius: \"22%\",\n /** Corner radius as a fraction of the icon size; `squircle` keeps the original 22%. */\n shapes: Object.freeze({ squircle: 0.22, square: 0, circle: 0.5 }) as Readonly<Record<IconTileShape, number>>,\n // Standalone tile activation must not add Button face padding around a narrow launcher cell.\n activation: Object.freeze({ minimumTarget: control.minTouchTarget, pressedOpacity: opacity.pressed, disabledOpacity: opacity.disabled }),\n fillMinSize: 48, fillMaxSize: 68,\n defaults: Object.freeze({ frame: \"surface\" as IconTileFrame, shape: \"squircle\" as IconTileShape, size: \"fixed\" as IconTileSize }) });\n\n/**\n * What an IconTile passes to an `icon` render function: the rendered icon edge (`iconSize` for `fixed`, the measured\n * cell-clamped edge for `fill`). Fixed-size artwork (images, generated launcher art) sizes itself from it; Utilverse\n * re-computed the 48–68 clamp from its grid width to size its artwork (2026-10-18).\n */\nexport type IconTileArtwork = Readonly<{ edge: number }>;\n\n/** The edge a `size=\"fill\"` icon takes in a cell of `cellWidth`, for layout estimates made before the tile measures. */\nexport function resolveIconTileFillEdge(cellWidth: number): number {\n if (!Number.isFinite(cellWidth) || cellWidth < 0) throw new RangeError(\"IconTile cell width must be a non-negative finite number\");\n return Math.min(iconTileRecipe.fillMaxSize, Math.max(iconTileRecipe.fillMinSize, cellWidth));\n}\n"]}
@@ -81,6 +81,11 @@ export declare const semanticColors: {
81
81
  key: "onPrimary";
82
82
  alpha?: number;
83
83
  }>;
84
+ readonly statusInverse: Readonly<{
85
+ source: "theme";
86
+ key: "onStatusFill";
87
+ alpha?: number;
88
+ }>;
84
89
  };
85
90
  readonly border: {
86
91
  readonly subtle: Readonly<{
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-colors.d.ts","sourceRoot":"","sources":["../src/semantic-colors.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;QAOvB;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAgBH,iDAAiD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4DjD;;;WAGG;;;;;;;CAGG,CAAC"}
1
+ {"version":3,"file":"semantic-colors.d.ts","sourceRoot":"","sources":["../src/semantic-colors.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;QAOvB;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAkBH,iDAAiD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4DjD;;;WAGG;;;;;;;CAGG,CAAC"}
@@ -26,6 +26,8 @@ export const semanticColors = {
26
26
  brand: themeColor("contentBrand"),
27
27
  danger: themeColor("danger"),
28
28
  inverse: themeColor("onPrimary"),
29
+ // Status fills are independent of a product's primary button contrast pair.
30
+ statusInverse: themeColor("onStatusFill"),
29
31
  },
30
32
  border: {
31
33
  subtle: themeColor("border", 0.7),
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-colors.js","sourceRoot":"","sources":["../src/semantic-colors.ts"],"names":[],"mappings":"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","sourcesContent":["import { accentColor, themeColor } from \"./color-references.js\";\n\n/**\n * Stable role aliases above the legacy ThemeColors key set. New recipes should\n * prefer these roles so the underlying theme shape can evolve independently.\n */\nexport const semanticColors = {\n canvas: themeColor(\"bg\"),\n surface: {\n default: themeColor(\"surface\"),\n sunken: themeColor(\"surfaceAlt\"),\n raised: themeColor(\"bg\"),\n brand: themeColor(\"surfaceAccent\"),\n /**\n * Loading placeholder fill (Skeleton, ListRow `loading`). A translucent text tint instead of an opaque\n * neutral: it darkens/lightens whatever it sits on, so it stays visible on canvas, Card surfaces and\n * profile palettes whose `surfaceAlt` is as light as `bg` (2026-10-17 Skeleton review, skeleton.md 근거).\n */\n placeholder: themeColor(\"text\", 0.12),\n },\n content: {\n primary: themeColor(\"text\"),\n body: themeColor(\"textBody\"),\n secondary: themeColor(\"textMuted\"),\n tertiary: themeColor(\"textSub\"),\n decorative: themeColor(\"textWeak\"),\n brand: themeColor(\"contentBrand\"),\n danger: themeColor(\"danger\"),\n inverse: themeColor(\"onPrimary\"),\n },\n border: {\n subtle: themeColor(\"border\", 0.7),\n default: themeColor(\"border\"),\n /** Resting outline of an interactive control. */\n control: themeColor(\"borderControl\"),\n strong: themeColor(\"textWeak\"),\n focus: themeColor(\"contentBrand\"),\n danger: themeColor(\"danger\"),\n },\n action: {\n brand: {\n background: themeColor(\"primary\"),\n content: themeColor(\"onPrimary\"),\n },\n neutral: {\n background: themeColor(\"bg\"),\n content: themeColor(\"text\"),\n // A control outline is a border role. `textSub` made a resting neutral\n // action read heavier than the selected control beside it.\n border: themeColor(\"borderControl\"),\n },\n danger: {\n background: themeColor(\"dangerFill\"),\n content: themeColor(\"onDanger\"),\n },\n },\n feedback: {\n info: {\n foreground: accentColor(\"info\"),\n background: accentColor(\"info\", 0.1),\n badgeBackground: accentColor(\"info\", 0.1),\n border: accentColor(\"info\", 0.3),\n },\n success: {\n foreground: accentColor(\"success\"),\n background: accentColor(\"success\", 0.1),\n badgeBackground: accentColor(\"success\", 0.1),\n border: accentColor(\"success\", 0.3),\n },\n warning: {\n foreground: accentColor(\"warning\"),\n background: accentColor(\"warning\", 0.1),\n badgeBackground: accentColor(\"warning\", 0.1),\n border: accentColor(\"warning\", 0.3),\n },\n attention: {\n foreground: accentColor(\"attention\"),\n background: accentColor(\"attention\", 0.1),\n badgeBackground: accentColor(\"attention\", 0.1),\n border: accentColor(\"attention\", 0.3),\n },\n danger: {\n foreground: themeColor(\"danger\"),\n background: themeColor(\"danger\", 0.1),\n badgeBackground: themeColor(\"danger\", 0.1),\n border: themeColor(\"danger\", 0.3),\n },\n },\n interaction: {\n hover: themeColor(\"text\", 0.06),\n focus: themeColor(\"contentBrand\", 0.08),\n pressed: themeColor(\"text\", 0.1),\n selected: themeColor(\"primary\", 0.1),\n /**\n * Lower edge of a raised object, composited over its own fill so one shade\n * darkens any brand color (BottomNavigation raised tile, Spint 2026-10-13).\n */\n depth: themeColor(\"text\", 0.45),\n },\n} as const;\n"]}
1
+ {"version":3,"file":"semantic-colors.js","sourceRoot":"","sources":["../src/semantic-colors.ts"],"names":[],"mappings":"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","sourcesContent":["import { accentColor, themeColor } from \"./color-references.js\";\n\n/**\n * Stable role aliases above the legacy ThemeColors key set. New recipes should\n * prefer these roles so the underlying theme shape can evolve independently.\n */\nexport const semanticColors = {\n canvas: themeColor(\"bg\"),\n surface: {\n default: themeColor(\"surface\"),\n sunken: themeColor(\"surfaceAlt\"),\n raised: themeColor(\"bg\"),\n brand: themeColor(\"surfaceAccent\"),\n /**\n * Loading placeholder fill (Skeleton, ListRow `loading`). A translucent text tint instead of an opaque\n * neutral: it darkens/lightens whatever it sits on, so it stays visible on canvas, Card surfaces and\n * profile palettes whose `surfaceAlt` is as light as `bg` (2026-10-17 Skeleton review, skeleton.md 근거).\n */\n placeholder: themeColor(\"text\", 0.12),\n },\n content: {\n primary: themeColor(\"text\"),\n body: themeColor(\"textBody\"),\n secondary: themeColor(\"textMuted\"),\n tertiary: themeColor(\"textSub\"),\n decorative: themeColor(\"textWeak\"),\n brand: themeColor(\"contentBrand\"),\n danger: themeColor(\"danger\"),\n inverse: themeColor(\"onPrimary\"),\n // Status fills are independent of a product's primary button contrast pair.\n statusInverse: themeColor(\"onStatusFill\"),\n },\n border: {\n subtle: themeColor(\"border\", 0.7),\n default: themeColor(\"border\"),\n /** Resting outline of an interactive control. */\n control: themeColor(\"borderControl\"),\n strong: themeColor(\"textWeak\"),\n focus: themeColor(\"contentBrand\"),\n danger: themeColor(\"danger\"),\n },\n action: {\n brand: {\n background: themeColor(\"primary\"),\n content: themeColor(\"onPrimary\"),\n },\n neutral: {\n background: themeColor(\"bg\"),\n content: themeColor(\"text\"),\n // A control outline is a border role. `textSub` made a resting neutral\n // action read heavier than the selected control beside it.\n border: themeColor(\"borderControl\"),\n },\n danger: {\n background: themeColor(\"dangerFill\"),\n content: themeColor(\"onDanger\"),\n },\n },\n feedback: {\n info: {\n foreground: accentColor(\"info\"),\n background: accentColor(\"info\", 0.1),\n badgeBackground: accentColor(\"info\", 0.1),\n border: accentColor(\"info\", 0.3),\n },\n success: {\n foreground: accentColor(\"success\"),\n background: accentColor(\"success\", 0.1),\n badgeBackground: accentColor(\"success\", 0.1),\n border: accentColor(\"success\", 0.3),\n },\n warning: {\n foreground: accentColor(\"warning\"),\n background: accentColor(\"warning\", 0.1),\n badgeBackground: accentColor(\"warning\", 0.1),\n border: accentColor(\"warning\", 0.3),\n },\n attention: {\n foreground: accentColor(\"attention\"),\n background: accentColor(\"attention\", 0.1),\n badgeBackground: accentColor(\"attention\", 0.1),\n border: accentColor(\"attention\", 0.3),\n },\n danger: {\n foreground: themeColor(\"danger\"),\n background: themeColor(\"danger\", 0.1),\n badgeBackground: themeColor(\"danger\", 0.1),\n border: themeColor(\"danger\", 0.3),\n },\n },\n interaction: {\n hover: themeColor(\"text\", 0.06),\n focus: themeColor(\"contentBrand\", 0.08),\n pressed: themeColor(\"text\", 0.1),\n selected: themeColor(\"primary\", 0.1),\n /**\n * Lower edge of a raised object, composited over its own fill so one shade\n * darkens any brand color (BottomNavigation raised tile, Spint 2026-10-13).\n */\n depth: themeColor(\"text\", 0.45),\n },\n} as const;\n"]}
package/dist/sheet.d.ts CHANGED
@@ -48,6 +48,21 @@ export declare const sheetDragRecipe: {
48
48
  /** px per millisecond: a quick flick closes before the distance threshold. */
49
49
  readonly dismissVelocity: 0.5;
50
50
  };
51
+ /**
52
+ * A persistent map panel folds to its handle instead of dismissing. Preserve the
53
+ * 14pt reading/paging arbitration observed in Diairy: taking a drag after native
54
+ * scrolling starts makes both the panel and its text move (2026-10-05 recording).
55
+ */
56
+ export declare const sheetPanelBehavior: {
57
+ readonly activationDistance: 14;
58
+ readonly collapseDistanceRatio: 0.5;
59
+ readonly collapseVelocity: 500;
60
+ readonly spring: {
61
+ readonly damping: 18;
62
+ readonly stiffness: 180;
63
+ readonly overshootClamping: true;
64
+ };
65
+ };
51
66
  /**
52
67
  * Resolves an attempted close without platform knowledge. Programmatic close is
53
68
  * intentionally an owner override: a controlled `open={false}` cannot be vetoed
@@ -1 +1 @@
1
- {"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../src/sheet.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,QAAQ,GACR,MAAM,GACN,SAAS,GACT,OAAO,GACP,cAAc,CAAC;AAEnB;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAAC;CACxC,CAAC,CAAC;AAEH,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GACtB,QAAQ,CAAC;IACP,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACnE,CAAC,GACF,QAAQ,CAAC;IACP,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACxE,CAAC,CAAC;AAEP,eAAO,MAAM,qBAAqB;;;;;;CAOK,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAO,CAAC,kBAAkB,CAAM,GAAG,kBAAkB,CAE5H;AACD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAEvF;AAED,mFAAmF;AACnF,eAAO,MAAM,eAAe;;;IAG1B,8EAA8E;;CAEtE,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,OAAO,EACb,MAAM,GAAE,kBAA0C,GACjD,OAAO,CAUT;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,IAAI,IAAI,MAAM,CAAC;IACf,YAAY,IAAI,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,CACV,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC;IACX,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC,CAAC,CAAC;AAEH;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,gBAAgB,UAAQ,GACvB,cAAc,CAgChB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtD,eAAO,MAAM,gBAAgB,sCAAwE,CAAC;AAEtG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAsB1E;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,QAAQ,GAAG,UAAU,GAC/B,WAAW,GAAG,IAAI,CASpB"}
1
+ {"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../src/sheet.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,QAAQ,GACR,MAAM,GACN,SAAS,GACT,OAAO,GACP,cAAc,CAAC;AAEnB;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAAC;CACxC,CAAC,CAAC;AAEH,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GACtB,QAAQ,CAAC;IACP,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACnE,CAAC,GACF,QAAQ,CAAC;IACP,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACxE,CAAC,CAAC;AAEP,eAAO,MAAM,qBAAqB;;;;;;CAOK,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAO,CAAC,kBAAkB,CAAM,GAAG,kBAAkB,CAE5H;AACD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAEvF;AAED,mFAAmF;AACnF,eAAO,MAAM,eAAe;;;IAG1B,8EAA8E;;CAEtE,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;CAKrB,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,OAAO,EACb,MAAM,GAAE,kBAA0C,GACjD,OAAO,CAUT;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,IAAI,IAAI,MAAM,CAAC;IACf,YAAY,IAAI,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,CACV,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC;IACX,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC,CAAC,CAAC;AAEH;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,gBAAgB,UAAQ,GACvB,cAAc,CAgChB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtD,eAAO,MAAM,gBAAgB,sCAAwE,CAAC;AAEtG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAsB1E;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,QAAQ,GAAG,UAAU,GAC/B,WAAW,GAAG,IAAI,CASpB"}
package/dist/sheet.js CHANGED
@@ -27,6 +27,17 @@ export const sheetDragRecipe = {
27
27
  /** px per millisecond: a quick flick closes before the distance threshold. */
28
28
  dismissVelocity: 0.5,
29
29
  };
30
+ /**
31
+ * A persistent map panel folds to its handle instead of dismissing. Preserve the
32
+ * 14pt reading/paging arbitration observed in Diairy: taking a drag after native
33
+ * scrolling starts makes both the panel and its text move (2026-10-05 recording).
34
+ */
35
+ export const sheetPanelBehavior = {
36
+ activationDistance: 14,
37
+ collapseDistanceRatio: 0.5,
38
+ collapseVelocity: 500,
39
+ spring: { damping: 18, stiffness: 180, overshootClamping: true },
40
+ };
30
41
  /**
31
42
  * Resolves an attempted close without platform knowledge. Programmatic close is
32
43
  * intentionally an owner override: a controlled `open={false}` cannot be vetoed
package/dist/sheet.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sheet.js","sourceRoot":"","sources":["../src/sheet.ts"],"names":[],"mappings":"AAsCA,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,WAAW,EAAE,IAAI;IACjB,gBAAgB,EAAE,KAAK;IACvB,cAAc,EAAE,IAAI;IACpB,mBAAmB,EAAE,IAAI;IACzB,wFAAwF;IACxF,YAAY,EAAE,IAAI;CACmB,CAAC;AAExC;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,UAAkB,EAAE,WAAwC,EAAE;IACtG,OAAO,EAAE,GAAG,qBAAqB,EAAE,GAAG,QAAQ,EAAE,CAAC;AACnD,CAAC;AACD,MAAM,UAAU,uBAAuB,CAAC,SAAiB,EAAE,SAAmB;IAC5E,OAAO,SAAS,IAAI,SAAS,KAAK,QAAQ,CAAC;AAC7C,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,kBAAkB,EAAE,CAAC;IACrB,oBAAoB,EAAE,IAAI;IAC1B,8EAA8E;IAC9E,eAAe,EAAE,GAAG;CACZ,CAAC;AAEX;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA0B,EAC1B,IAAa,EACb,SAA6B,qBAAqB;IAElD,IAAI,MAAM,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,cAAc,CAAC;IACvD,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC,mBAAmB,CAAC;IACpC,CAAC;IACD,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,MAAM,CAAC,YAAY,CAAC;IACnD,OAAO,IAAI,CAAC;AACd,CAAC;AAaD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,gBAAgB,GAAG,KAAK;IAExB,IAAI,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,IAAI,WAAW,GAAkB,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE3C,OAAO;QACL,IAAI;YACF,IAAI,WAAW,KAAK,IAAI;gBAAE,OAAO,WAAW,CAAC;YAC7C,QAAQ,IAAI,CAAC,CAAC;YACd,WAAW,GAAG,QAAQ,CAAC;YACvB,cAAc,GAAG,KAAK,CAAC;YACvB,OAAO,WAAW,CAAC;QACrB,CAAC;QACD,YAAY;YACV,IAAI,WAAW,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAC;YACtC,MAAM,KAAK,GAAG,WAAW,CAAC;YAC1B,WAAW,GAAG,IAAI,CAAC;YACnB,cAAc,GAAG,KAAK,CAAC;YACvB,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC5B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB;YACvD,IAAI,WAAW,KAAK,IAAI,IAAI,cAAc;gBAAE,OAAO,KAAK,CAAC;YACzD,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC;gBAAE,OAAO,KAAK,CAAC;YACzD,cAAc,GAAG,IAAI,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,eAAe,CAAC,KAAK;YACnB,OAAO,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC;AAgBD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAA2C,CAAC;AAEtG,MAAM,UAAU,oBAAoB,CAAC,OAA+B;IAClE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,UAAU,CAAC,iCAAiC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAe,CAAC;IACpC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,SAAS,CAAC,6BAA6B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,2BAA2B,MAAM,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAC/B,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAClF,CAAC;IACF,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAC/D;;;UAGE;QACF,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;IAC/E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,OAA+B,EAC/B,OAAoB,EACpB,SAAgC;IAEhC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,UAAU,CAAC,4CAA4C,OAAO,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IAC5D,0DAA0D;IAC1D,OAAO,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAE,CAAC;AACpE,CAAC","sourcesContent":["/** Why an open Sheet is being asked to close. */\nexport type SheetDismissReason =\n | \"close-action\"\n | \"escape\"\n | \"back\"\n | \"outside\"\n | \"swipe\"\n | \"programmatic\";\n\n/**\n * `trigger` only opens a Sheet. Every close reports the concrete dismiss reason\n * so renderers and product analytics do not have to infer it from platform events.\n */\nexport type SheetOpenChangeDetails = Readonly<{\n reason: \"trigger\" | SheetDismissReason;\n}>;\n\n/** User-dismiss policy. A controlled owner may always close programmatically. */\nexport type SheetDismissPolicy = Readonly<{\n dismissible: boolean;\n dismissWhileBusy: boolean;\n outsideDismiss: boolean;\n escapeOrBackDismiss: boolean;\n swipeDismiss: boolean;\n}>;\n\nexport type SheetOpenState =\n | Readonly<{\n open: boolean;\n defaultOpen?: never;\n onOpenChange(open: boolean, detail: SheetOpenChangeDetails): void;\n }>\n | Readonly<{\n open?: never;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: SheetOpenChangeDetails) => void;\n }>;\n\nexport const sheetBehaviorDefaults = {\n dismissible: true,\n dismissWhileBusy: false,\n outsideDismiss: true,\n escapeOrBackDismiss: true,\n // 2026-10-16 user decision: dragging a bottom sheet down is a required way to close it.\n swipeDismiss: true,\n} as const satisfies SheetDismissPolicy;\n\n/**\n * Every placement closes on a backdrop press (2026-10-17 user decision; bottom sheets had been drag-only\n * since 2026-10-16). Bottom sheets still show no × by default: drag, backdrop, Escape/system back and the\n * handle's close action cover pointer, keyboard, switch and screen-reader users. `outsideDismiss: false`\n * (with `swipeDismiss: false`) is only for flows where an accidental close loses work, such as payment\n * confirmation; unsaved-input confirmation belongs in the controlled owner's `onOpenChange`, which every\n * reason, `outside` included, passes through. `placement` stays in the signature for published callers.\n */\nexport function resolveSheetDismissPolicy(_placement: string, override: Partial<SheetDismissPolicy> = {}): SheetDismissPolicy {\n return { ...sheetBehaviorDefaults, ...override };\n}\nexport function resolveSheetCloseButton(placement: string, showClose?: boolean): boolean {\n return showClose ?? placement !== \"bottom\";\n}\n\n/** Release thresholds for the drag-to-dismiss gesture shared by both renderers. */\nexport const sheetDragRecipe = {\n activationDistance: 6,\n dismissDistanceRatio: 0.25,\n /** px per millisecond: a quick flick closes before the distance threshold. */\n dismissVelocity: 0.5,\n} as const;\n\n/**\n * Resolves an attempted close without platform knowledge. Programmatic close is\n * intentionally an owner override: a controlled `open={false}` cannot be vetoed\n * by a renderer, even while the Sheet is busy or not user-dismissible.\n */\nexport function canDismissSheet(\n reason: SheetDismissReason,\n busy: boolean,\n policy: SheetDismissPolicy = sheetBehaviorDefaults,\n): boolean {\n if (reason === \"programmatic\") return true;\n if (!policy.dismissible) return false;\n if (busy && !policy.dismissWhileBusy) return false;\n if (reason === \"outside\") return policy.outsideDismiss;\n if (reason === \"escape\" || reason === \"back\") {\n return policy.escapeOrBackDismiss;\n }\n if (reason === \"swipe\") return policy.swipeDismiss;\n return true;\n}\n\nexport type SheetLifecycle = Readonly<{\n open(): number;\n beginDismiss(): number | null;\n requestClose(\n reason: SheetDismissReason,\n busy: boolean,\n policy?: SheetDismissPolicy,\n ): boolean;\n completeDismiss(cycle: number): boolean;\n}>;\n\n/**\n * Makes persistent native Modal renderers settle each visible cycle once.\n * Platform adapters still decide when native dismissal has actually completed.\n */\nexport function createSheetLifecycle(\n initiallyVisible = false,\n): SheetLifecycle {\n let sequence = initiallyVisible ? 1 : 0;\n let activeCycle: number | null = initiallyVisible ? sequence : null;\n let closeRequested = false;\n const dismissingCycles = new Set<number>();\n\n return {\n open() {\n if (activeCycle !== null) return activeCycle;\n sequence += 1;\n activeCycle = sequence;\n closeRequested = false;\n return activeCycle;\n },\n beginDismiss() {\n if (activeCycle === null) return null;\n const cycle = activeCycle;\n activeCycle = null;\n closeRequested = false;\n dismissingCycles.add(cycle);\n return cycle;\n },\n requestClose(reason, busy, policy = sheetBehaviorDefaults) {\n if (activeCycle === null || closeRequested) return false;\n if (!canDismissSheet(reason, busy, policy)) return false;\n closeRequested = true;\n return true;\n },\n completeDismiss(cycle) {\n return dismissingCycles.delete(cycle);\n },\n };\n}\n\n/**\n * 사용자가 시트 높이를 바꾸는 축이다. `sheetRecipe.sizes`는 제품이 여는 높이를 정하고,\n * 이것은 열린 뒤 **사용자가** 단계를 옮기는 문제다 — 지도 위 목록처럼 \"조금 보기 ↔ 크게\n * 보기\"가 필요한 화면이 그 자리다.\n *\n * `auto`는 단계에 들어가지 않는다. 내용이 정하는 높이라 \"다음 단계\"가 정의되지 않는다.\n *\n * **드래그 물리는 여기 없다.** 이 계약은 단계 목록과 \"다음/이전 단계가 무엇인가\"만 갖고,\n * 손가락 속도·관성·고무줄은 넣지 않았다 — 실제 제품 화면 없이 그 값을 정하면 기기에서\n * 다시 다 고치게 된다. 대신 **핸들이 접근 가능한 컨트롤**이라 키보드·보조기기에서도\n * 단계를 옮길 수 있다. 제스처는 제품이 얹고, 결과를 같은 `onDetentChange`로 보낸다.\n */\nexport type SheetDetent = \"medium\" | \"large\" | \"full\";\n\nexport const sheetDetentOrder = [\"medium\", \"large\", \"full\"] as const satisfies readonly SheetDetent[];\n\nexport function validateSheetDetents(detents: readonly SheetDetent[]): void {\n if (detents.length === 0) {\n throw new RangeError(\"Sheet detents must not be empty\");\n }\n const seen = new Set<SheetDetent>();\n for (const detent of detents) {\n if (!sheetDetentOrder.includes(detent)) {\n throw new TypeError(`Unsupported Sheet detent: ${String(detent)}`);\n }\n if (seen.has(detent)) throw new TypeError(`Duplicate Sheet detent: ${detent}`);\n seen.add(detent);\n }\n const ordered = [...detents].sort(\n (left, right) => sheetDetentOrder.indexOf(left) - sheetDetentOrder.indexOf(right),\n );\n if (ordered.some((detent, index) => detent !== detents[index])) {\n /*\n 정렬을 대신 해 주지 않는 이유: 순서가 곧 \"다음 단계\"의 의미라서, 제품이 적은 순서와\n 실제 이동 순서가 다르면 디버깅할 수 없는 화면이 된다.\n */\n throw new TypeError(\"Sheet detents must be listed from smallest to largest\");\n }\n}\n\nexport function resolveNextSheetDetent(\n detents: readonly SheetDetent[],\n current: SheetDetent,\n direction: \"expand\" | \"collapse\",\n): SheetDetent | null {\n validateSheetDetents(detents);\n const index = detents.indexOf(current);\n if (index === -1) {\n throw new RangeError(`Sheet current detent is not in the list: ${current}`);\n }\n const next = direction === \"expand\" ? index + 1 : index - 1;\n /** 끝에서는 `null`이다 — renderer가 \"더 갈 수 없다\"를 컨트롤 상태로 표현한다. */\n return next < 0 || next >= detents.length ? null : detents[next]!;\n}\n\n"]}
1
+ {"version":3,"file":"sheet.js","sourceRoot":"","sources":["../src/sheet.ts"],"names":[],"mappings":"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","sourcesContent":["/** Why an open Sheet is being asked to close. */\nexport type SheetDismissReason =\n | \"close-action\"\n | \"escape\"\n | \"back\"\n | \"outside\"\n | \"swipe\"\n | \"programmatic\";\n\n/**\n * `trigger` only opens a Sheet. Every close reports the concrete dismiss reason\n * so renderers and product analytics do not have to infer it from platform events.\n */\nexport type SheetOpenChangeDetails = Readonly<{\n reason: \"trigger\" | SheetDismissReason;\n}>;\n\n/** User-dismiss policy. A controlled owner may always close programmatically. */\nexport type SheetDismissPolicy = Readonly<{\n dismissible: boolean;\n dismissWhileBusy: boolean;\n outsideDismiss: boolean;\n escapeOrBackDismiss: boolean;\n swipeDismiss: boolean;\n}>;\n\nexport type SheetOpenState =\n | Readonly<{\n open: boolean;\n defaultOpen?: never;\n onOpenChange(open: boolean, detail: SheetOpenChangeDetails): void;\n }>\n | Readonly<{\n open?: never;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: SheetOpenChangeDetails) => void;\n }>;\n\nexport const sheetBehaviorDefaults = {\n dismissible: true,\n dismissWhileBusy: false,\n outsideDismiss: true,\n escapeOrBackDismiss: true,\n // 2026-10-16 user decision: dragging a bottom sheet down is a required way to close it.\n swipeDismiss: true,\n} as const satisfies SheetDismissPolicy;\n\n/**\n * Every placement closes on a backdrop press (2026-10-17 user decision; bottom sheets had been drag-only\n * since 2026-10-16). Bottom sheets still show no × by default: drag, backdrop, Escape/system back and the\n * handle's close action cover pointer, keyboard, switch and screen-reader users. `outsideDismiss: false`\n * (with `swipeDismiss: false`) is only for flows where an accidental close loses work, such as payment\n * confirmation; unsaved-input confirmation belongs in the controlled owner's `onOpenChange`, which every\n * reason, `outside` included, passes through. `placement` stays in the signature for published callers.\n */\nexport function resolveSheetDismissPolicy(_placement: string, override: Partial<SheetDismissPolicy> = {}): SheetDismissPolicy {\n return { ...sheetBehaviorDefaults, ...override };\n}\nexport function resolveSheetCloseButton(placement: string, showClose?: boolean): boolean {\n return showClose ?? placement !== \"bottom\";\n}\n\n/** Release thresholds for the drag-to-dismiss gesture shared by both renderers. */\nexport const sheetDragRecipe = {\n activationDistance: 6,\n dismissDistanceRatio: 0.25,\n /** px per millisecond: a quick flick closes before the distance threshold. */\n dismissVelocity: 0.5,\n} as const;\n\n/**\n * A persistent map panel folds to its handle instead of dismissing. Preserve the\n * 14pt reading/paging arbitration observed in Diairy: taking a drag after native\n * scrolling starts makes both the panel and its text move (2026-10-05 recording).\n */\nexport const sheetPanelBehavior = {\n activationDistance: 14,\n collapseDistanceRatio: 0.5,\n collapseVelocity: 500,\n spring: { damping: 18, stiffness: 180, overshootClamping: true },\n} as const;\n\n/**\n * Resolves an attempted close without platform knowledge. Programmatic close is\n * intentionally an owner override: a controlled `open={false}` cannot be vetoed\n * by a renderer, even while the Sheet is busy or not user-dismissible.\n */\nexport function canDismissSheet(\n reason: SheetDismissReason,\n busy: boolean,\n policy: SheetDismissPolicy = sheetBehaviorDefaults,\n): boolean {\n if (reason === \"programmatic\") return true;\n if (!policy.dismissible) return false;\n if (busy && !policy.dismissWhileBusy) return false;\n if (reason === \"outside\") return policy.outsideDismiss;\n if (reason === \"escape\" || reason === \"back\") {\n return policy.escapeOrBackDismiss;\n }\n if (reason === \"swipe\") return policy.swipeDismiss;\n return true;\n}\n\nexport type SheetLifecycle = Readonly<{\n open(): number;\n beginDismiss(): number | null;\n requestClose(\n reason: SheetDismissReason,\n busy: boolean,\n policy?: SheetDismissPolicy,\n ): boolean;\n completeDismiss(cycle: number): boolean;\n}>;\n\n/**\n * Makes persistent native Modal renderers settle each visible cycle once.\n * Platform adapters still decide when native dismissal has actually completed.\n */\nexport function createSheetLifecycle(\n initiallyVisible = false,\n): SheetLifecycle {\n let sequence = initiallyVisible ? 1 : 0;\n let activeCycle: number | null = initiallyVisible ? sequence : null;\n let closeRequested = false;\n const dismissingCycles = new Set<number>();\n\n return {\n open() {\n if (activeCycle !== null) return activeCycle;\n sequence += 1;\n activeCycle = sequence;\n closeRequested = false;\n return activeCycle;\n },\n beginDismiss() {\n if (activeCycle === null) return null;\n const cycle = activeCycle;\n activeCycle = null;\n closeRequested = false;\n dismissingCycles.add(cycle);\n return cycle;\n },\n requestClose(reason, busy, policy = sheetBehaviorDefaults) {\n if (activeCycle === null || closeRequested) return false;\n if (!canDismissSheet(reason, busy, policy)) return false;\n closeRequested = true;\n return true;\n },\n completeDismiss(cycle) {\n return dismissingCycles.delete(cycle);\n },\n };\n}\n\n/**\n * 사용자가 시트 높이를 바꾸는 축이다. `sheetRecipe.sizes`는 제품이 여는 높이를 정하고,\n * 이것은 열린 뒤 **사용자가** 단계를 옮기는 문제다 — 지도 위 목록처럼 \"조금 보기 ↔ 크게\n * 보기\"가 필요한 화면이 그 자리다.\n *\n * `auto`는 단계에 들어가지 않는다. 내용이 정하는 높이라 \"다음 단계\"가 정의되지 않는다.\n *\n * **드래그 물리는 여기 없다.** 이 계약은 단계 목록과 \"다음/이전 단계가 무엇인가\"만 갖고,\n * 손가락 속도·관성·고무줄은 넣지 않았다 — 실제 제품 화면 없이 그 값을 정하면 기기에서\n * 다시 다 고치게 된다. 대신 **핸들이 접근 가능한 컨트롤**이라 키보드·보조기기에서도\n * 단계를 옮길 수 있다. 제스처는 제품이 얹고, 결과를 같은 `onDetentChange`로 보낸다.\n */\nexport type SheetDetent = \"medium\" | \"large\" | \"full\";\n\nexport const sheetDetentOrder = [\"medium\", \"large\", \"full\"] as const satisfies readonly SheetDetent[];\n\nexport function validateSheetDetents(detents: readonly SheetDetent[]): void {\n if (detents.length === 0) {\n throw new RangeError(\"Sheet detents must not be empty\");\n }\n const seen = new Set<SheetDetent>();\n for (const detent of detents) {\n if (!sheetDetentOrder.includes(detent)) {\n throw new TypeError(`Unsupported Sheet detent: ${String(detent)}`);\n }\n if (seen.has(detent)) throw new TypeError(`Duplicate Sheet detent: ${detent}`);\n seen.add(detent);\n }\n const ordered = [...detents].sort(\n (left, right) => sheetDetentOrder.indexOf(left) - sheetDetentOrder.indexOf(right),\n );\n if (ordered.some((detent, index) => detent !== detents[index])) {\n /*\n 정렬을 대신 해 주지 않는 이유: 순서가 곧 \"다음 단계\"의 의미라서, 제품이 적은 순서와\n 실제 이동 순서가 다르면 디버깅할 수 없는 화면이 된다.\n */\n throw new TypeError(\"Sheet detents must be listed from smallest to largest\");\n }\n}\n\nexport function resolveNextSheetDetent(\n detents: readonly SheetDetent[],\n current: SheetDetent,\n direction: \"expand\" | \"collapse\",\n): SheetDetent | null {\n validateSheetDetents(detents);\n const index = detents.indexOf(current);\n if (index === -1) {\n throw new RangeError(`Sheet current detent is not in the list: ${current}`);\n }\n const next = direction === \"expand\" ? index + 1 : index - 1;\n /** 끝에서는 `null`이다 — renderer가 \"더 갈 수 없다\"를 컨트롤 상태로 표현한다. */\n return next < 0 || next >= detents.length ? null : detents[next]!;\n}\n\n"]}
@@ -0,0 +1,20 @@
1
+ export type ShortcutRailItem = Readonly<{
2
+ id: string;
3
+ label: string;
4
+ description?: string;
5
+ disabled?: boolean;
6
+ }>;
7
+ export declare const shortcutRailRecipe: {
8
+ readonly defaults: {
9
+ readonly layout: "scroll";
10
+ };
11
+ readonly gap: 8;
12
+ readonly glyph: 14;
13
+ readonly actionMinWidth: 44;
14
+ readonly removeWidth: 44;
15
+ readonly minTouchTarget: 44;
16
+ };
17
+ export declare function validateShortcutRail(items: readonly ShortcutRailItem[], label: string, layout?: "scroll" | "wrap"): void;
18
+ /** Minimal physical offset that exposes a focused shortcut in either reading direction. */
19
+ export declare function revealShortcut(offset: number, viewport: number, start: number, width: number): number;
20
+ //# sourceMappingURL=shortcut-rail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortcut-rail.d.ts","sourceRoot":"","sources":["../src/shortcut-rail.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAIjH,eAAO,MAAM,kBAAkB;;;;;;;;;CAOrB,CAAC;AAEX,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,QAAQ,GAAG,MAAiB,QAI3H;AAED,2FAA2F;AAC3F,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,UAI5F"}
@@ -0,0 +1,28 @@
1
+ import { validateItems } from "./interaction-adapters.js";
2
+ import { control, glyph, spacing } from "./foundations.js";
3
+ // Recent cities need independent open/remove targets, not nested buttons or selected filters.
4
+ // Chip owns the pill's appearance; this composition adds finite horizontal placement and removal.
5
+ export const shortcutRailRecipe = {
6
+ defaults: { layout: "scroll" },
7
+ gap: spacing.xs,
8
+ glyph: glyph.xs,
9
+ actionMinWidth: control.minTouchTarget,
10
+ removeWidth: control.minTouchTarget,
11
+ minTouchTarget: control.minTouchTarget,
12
+ };
13
+ export function validateShortcutRail(items, label, layout = "scroll") {
14
+ if (layout !== "scroll" && layout !== "wrap")
15
+ throw new RangeError("ShortcutRail layout must be scroll or wrap");
16
+ validateItems(items);
17
+ if (!label.trim())
18
+ throw new TypeError("ShortcutRail label must not be empty");
19
+ }
20
+ /** Minimal physical offset that exposes a focused shortcut in either reading direction. */
21
+ export function revealShortcut(offset, viewport, start, width) {
22
+ if (start < offset)
23
+ return Math.max(0, start);
24
+ if (start + width > offset + viewport)
25
+ return Math.max(0, start + width - viewport);
26
+ return offset;
27
+ }
28
+ //# sourceMappingURL=shortcut-rail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortcut-rail.js","sourceRoot":"","sources":["../src/shortcut-rail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAI3D,8FAA8F;AAC9F,kGAAkG;AAClG,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;IAC9B,GAAG,EAAE,OAAO,CAAC,EAAE;IACf,KAAK,EAAE,KAAK,CAAC,EAAE;IACf,cAAc,EAAE,OAAO,CAAC,cAAc;IACtC,WAAW,EAAE,OAAO,CAAC,cAAc;IACnC,cAAc,EAAE,OAAO,CAAC,cAAc;CAC9B,CAAC;AAEX,MAAM,UAAU,oBAAoB,CAAC,KAAkC,EAAE,KAAa,EAAE,SAA4B,QAAQ;IAC1H,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM;QAAE,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,CAAC;IACjH,aAAa,CAAC,KAAK,CAAC,CAAC;IACrB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;AACjF,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,QAAgB,EAAE,KAAa,EAAE,KAAa;IAC3F,IAAI,KAAK,GAAG,MAAM;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9C,IAAI,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC,CAAC;IACpF,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { validateItems } from \"./interaction-adapters.js\";\nimport { control, glyph, spacing } from \"./foundations.js\";\n\nexport type ShortcutRailItem = Readonly<{ id: string; label: string; description?: string; disabled?: boolean }>;\n\n// Recent cities need independent open/remove targets, not nested buttons or selected filters.\n// Chip owns the pill's appearance; this composition adds finite horizontal placement and removal.\nexport const shortcutRailRecipe = {\n defaults: { layout: \"scroll\" },\n gap: spacing.xs,\n glyph: glyph.xs,\n actionMinWidth: control.minTouchTarget,\n removeWidth: control.minTouchTarget,\n minTouchTarget: control.minTouchTarget,\n} as const;\n\nexport function validateShortcutRail(items: readonly ShortcutRailItem[], label: string, layout: \"scroll\" | \"wrap\" = \"scroll\") {\n if (layout !== \"scroll\" && layout !== \"wrap\") throw new RangeError(\"ShortcutRail layout must be scroll or wrap\");\n validateItems(items);\n if (!label.trim()) throw new TypeError(\"ShortcutRail label must not be empty\");\n}\n\n/** Minimal physical offset that exposes a focused shortcut in either reading direction. */\nexport function revealShortcut(offset: number, viewport: number, start: number, width: number) {\n if (start < offset) return Math.max(0, start);\n if (start + width > offset + viewport) return Math.max(0, start + width - viewport);\n return offset;\n}\n"]}
package/dist/showcase.js CHANGED
@@ -168,7 +168,7 @@ const semanticOnlyBehaviors = new Set([
168
168
  const webKeyboardBehaviors = new Set([
169
169
  "agreement", "alertDialog", "anchor", "authProviderButton", "bottomNavigation", "breadcrumb",
170
170
  "calendar", "carousel", "chart", "checkbox", "checkboxGroup", "chip", "collapsible", "combobox",
171
- "commandPalette", "contextMenu", "dataTable", "datePicker", "dateRange", "dialog",
171
+ "commandPalette", "contextMenu", "dataTable", "datePicker", "dateRange", "timePicker", "dialog",
172
172
  "disclosureGroup", "field", "filePicker", "floatingActionButton", "form", "layout", "link",
173
173
  "loadMore", "menu", "menubar", "numberField", "otpField", "pagination", "passwordField",
174
174
  "popover", "radioGroup", "searchField", "segmentedControl", "select", "sheet", "sidePanel",
@@ -177,7 +177,7 @@ const webKeyboardBehaviors = new Set([
177
177
  ]);
178
178
  const nativeActionBehaviors = new Set([
179
179
  "agreement", "alertDialog", "authProviderButton", "bottomNavigation", "calendar", "carousel",
180
- "checkbox", "checkboxGroup", "chip", "collapsible", "combobox", "datePicker", "dateRange",
180
+ "checkbox", "checkboxGroup", "chip", "collapsible", "combobox", "datePicker", "dateRange", "timePicker",
181
181
  "dialog", "disclosureGroup", "field", "filePicker", "floatingActionButton", "form", "link",
182
182
  "loadMore", "menu", "numberField", "otpField", "passwordField", "radioGroup", "searchField",
183
183
  "segmentedControl", "select", "sheet", "slider", "switch", "tabs", "tagsInput", "toast",