@satinhq/kit 0.1.0-beta.2 → 0.1.0-beta.3

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 (104) hide show
  1. package/NOTICE +2 -2
  2. package/README.md +1 -1
  3. package/dist/acknowledgements/inventory.json +5 -5
  4. package/dist/actions/RowActions.d.ts +2 -2
  5. package/dist/actions/RowActions.js +2 -2
  6. package/dist/controls/civilDate.d.ts +1 -1
  7. package/dist/controls/civilDate.js +1 -1
  8. package/dist/docs/components/file-importer.md +1 -1
  9. package/dist/docs/components/paste-button.md +1 -1
  10. package/dist/docs/components/photos-picker.md +1 -1
  11. package/dist/docs/components/share-link.md +1 -1
  12. package/dist/docs/limitations.md +1 -1
  13. package/dist/docs/push.md +3 -3
  14. package/dist/docs/reference.md +1870 -0
  15. package/dist/glass/lens/controlLift.d.ts +1 -1
  16. package/dist/glass/lens/controlLift.js +1 -1
  17. package/dist/glass/lens/interactivePress.d.ts +2 -2
  18. package/dist/glass/lens/interactivePress.js +2 -2
  19. package/dist/glass/lens/menu-opening-native-fits.json +1 -1
  20. package/dist/glass/lens/menuContextFit.json +1 -1
  21. package/dist/glass/lens/menuDropFit.json +1 -1
  22. package/dist/glass/lens/menuField.d.ts +2 -2
  23. package/dist/glass/lens/menuField.js +3 -3
  24. package/dist/glass/lens/menuGlyphDropFit.json +1 -1
  25. package/dist/glass/lens/menuMorphFit.json +1 -1
  26. package/dist/glass/lens/menuMorphPhoneFit.json +1 -1
  27. package/dist/glass/lens/tabAccessoryMotion.d.ts +1 -1
  28. package/dist/glass/lens/tabSearchMember.d.ts +1 -1
  29. package/dist/glass/lens/tabSearchMember.js +1 -1
  30. package/dist/glass/menuPresenter.js +1 -1
  31. package/dist/glass/regions.d.ts +1 -1
  32. package/dist/glass/regions.js +1 -1
  33. package/dist/glass/shapeTrack.d.ts +1 -1
  34. package/dist/glass/submenuMotion.d.ts +1 -1
  35. package/dist/glass/submenuMotion.js +1 -1
  36. package/dist/glass/webgpu/entry.worker.js +3 -6
  37. package/dist/glass/webgpu/frameClock.d.ts +6 -2
  38. package/dist/glass/webgpu/frameClock.js +9 -2
  39. package/dist/glass/webgpu/frameProbe.d.ts +1 -1
  40. package/dist/glass/webgpu/frameProbe.js +3 -3
  41. package/dist/glass/webgpu/production.js +4 -0
  42. package/dist/glass/webgpu/raster.d.ts +18 -0
  43. package/dist/glass/webgpu/raster.js +105 -15
  44. package/dist/glass/webgpu/rasterMaterial.d.ts +4 -0
  45. package/dist/glass/webgpu/rasterMaterial.js +17 -1
  46. package/dist/glass/webgpu/regionScene.d.ts +4 -1
  47. package/dist/glass/webgpu/regionScene.js +30 -1
  48. package/dist/glass/webgpu/renderer.js +23 -7
  49. package/dist/keyboard/adapter.js +2 -2
  50. package/dist/keyboard/avoidance.d.ts +1 -1
  51. package/dist/keyboard/avoidance.js +1 -1
  52. package/dist/keyboard/motion.d.ts +1 -1
  53. package/dist/keyboard/motion.js +1 -1
  54. package/dist/list-form/ListForm.d.ts +2 -2
  55. package/dist/list-form/ListForm.js +1 -1
  56. package/dist/list-form/ListItem.js +1 -1
  57. package/dist/list-form/RowLabel.d.ts +1 -1
  58. package/dist/list-form/RowLabel.js +1 -1
  59. package/dist/list-form/Slider.js +1 -1
  60. package/dist/list-form/editMotion.js +1 -1
  61. package/dist/list-form/editing.d.ts +5 -5
  62. package/dist/list-form/editing.js +1 -1
  63. package/dist/motion/animationValue.d.ts +2 -2
  64. package/dist/motion/animationValue.js +9 -10
  65. package/dist/motion/matchedGeometry.d.ts +4 -4
  66. package/dist/motion/matchedGeometry.js +7 -8
  67. package/dist/motion/springTrack.d.ts +1 -2
  68. package/dist/motion/springTrack.js +10 -5
  69. package/dist/navigation/ScreenContainer.js +1 -1
  70. package/dist/navigation/chromeCurves.js +1 -1
  71. package/dist/navigation/types.d.ts +1 -1
  72. package/dist/navigation/zoomGeometry.js +1 -1
  73. package/dist/navigation/zoomMotion.d.ts +1 -1
  74. package/dist/navigation/zoomMotion.js +2 -2
  75. package/dist/navigation/zoomTransition.js +3 -4
  76. package/dist/presentation/Presentation.d.ts +2 -2
  77. package/dist/presentation/Presentation.js +1 -1
  78. package/dist/presentation/presentationSource.js +1 -1
  79. package/dist/public/controls.d.ts +4 -4
  80. package/dist/public/controls.js +7 -7
  81. package/dist/public/glass.d.ts +3 -4
  82. package/dist/public/glass.js +1 -2
  83. package/dist/public/navigationTransition.d.ts +1 -1
  84. package/dist/public/navigationTransition.js +1 -1
  85. package/dist/public/scroll.d.ts +1 -1
  86. package/dist/public/scroll.js +1 -1
  87. package/dist/public/toolbar.js +1 -1
  88. package/dist/safe-area/SafeAreaInset.js +3 -3
  89. package/dist/safe-area/modifiers.js +2 -2
  90. package/dist/safe-area/useSafeAreaMetrics.js +2 -2
  91. package/dist/scroll/ScrollSurface.d.ts +5 -5
  92. package/dist/scroll/ScrollSurface.js +1 -1
  93. package/dist/scroll/position.js +1 -1
  94. package/dist/scroll/screenGeometry.d.ts +1 -1
  95. package/dist/scroll/screenGeometry.js +1 -1
  96. package/dist/search/SearchDrawer.js +1 -1
  97. package/dist/search/searchMotion.d.ts +1 -1
  98. package/dist/search/searchMotion.js +1 -1
  99. package/dist/search/useSearchPresentation.js +1 -1
  100. package/dist/shapes/corners.js +1 -1
  101. package/dist/styles.css +55 -34
  102. package/dist/text-input/TextEditor.js +7 -3
  103. package/package.json +4 -4
  104. package/dist/docs/swiftui-reference.md +0 -1895
package/dist/styles.css CHANGED
@@ -922,7 +922,7 @@ body[data-satin-document='true'],
922
922
  gap: 8px;
923
923
  margin-top: 16px;
924
924
  }
925
- /* SwiftUI ContentUnavailableView fixture: 40.5pt symbol, 20pt label gap,
925
+ /* ContentUnavailableView geometry: 40.5pt symbol, 20pt label gap,
926
926
  * 3pt description gap and 30pt to the 15pt action label. Keep its 44pt hit area. */
927
927
  [data-satin-offline='full-screen'] {
928
928
  padding: 20px;
@@ -2972,7 +2972,7 @@ body[data-satin-document='true'],
2972
2972
  [data-satin-toolbar-item] {
2973
2973
  display: contents;
2974
2974
  }
2975
- /* SwiftUI ToolbarSpacer: a fixed spacer leaves the standard 12 pt between two items' glass
2975
+ /* ToolbarSpacer: a fixed spacer leaves the standard 12 pt between two items' glass
2976
2976
  (iOS 27.0 native reference: 44 pt circles at 320 and 376; bottom bar 48 pt circles at 166 and 226). */
2977
2977
  [data-satin-toolbar-spacer='fixed'] {
2978
2978
  width: 12px;
@@ -3349,14 +3349,14 @@ body[data-satin-document='true'],
3349
3349
  --satin-section-header-size: 17px;
3350
3350
  --satin-section-header-line-height: 20.333333px;
3351
3351
  --satin-section-header-weight: 600;
3352
- /* SwiftUI secondary foreground is half-opacity primary over the cell fill. */
3352
+ /* Secondary foreground uses half-opacity primary over the cell fill. */
3353
3353
  --satin-list-secondary: rgb(0 0 0 / 50%);
3354
3354
  }
3355
3355
  [data-satin-list] {
3356
3356
  width: 100%;
3357
3357
  color: var(--satin-list-label, #000);
3358
3358
  }
3359
- /* A List is flexible like SwiftUI's: in a stack it takes the remaining space (reference nested-form pair). */
3359
+ /* A List is flexible: in a stack it takes the remaining space (reference nested-form pair). */
3360
3360
  :where([data-satin-list]) {
3361
3361
  --satin-list-row-min-height: var(--satin-settings-root-row-height, 52px);
3362
3362
  flex-grow: 1;
@@ -3452,7 +3452,7 @@ body[data-satin-document='true'],
3452
3452
  [data-satin-list='plain'] [data-satin-section-rows='true'] {
3453
3453
  border-radius: 0;
3454
3454
  }
3455
- /* A grouped List paints systemGroupedBackground wherever it is placed, as SwiftUI's does, including a
3455
+ /* A grouped List paints systemGroupedBackground wherever it is placed, including a
3456
3456
  Form nested below other content (reference nested-form pairs). The status band samples it. */
3457
3457
  [data-satin-list='insetGrouped']:not(:where([data-satin-scroll-content-background='hidden'])),
3458
3458
  [data-satin-list='grouped']:not(:where([data-satin-scroll-content-background='hidden'])) {
@@ -3465,7 +3465,7 @@ body[data-satin-document='true'],
3465
3465
  background: #000;
3466
3466
  }
3467
3467
  /* Inside a sheet the sheet's own material is the one surface (native iOS 27 native reference, iPhone Form
3468
- sheet): a grouped List paints no background of its own. At medium the sheet is Liquid Glass and
3468
+ sheet): a grouped List paints no background of its own. At medium the sheet is glass and
3469
3469
  its cells are a grey fill on it (118 118 128: light 216 on 231 at 12%, dark 61 on 46 at 22%),
3470
3470
  separators 195 / 89 and off switch tracks 138 / 112 on them; at large it is opaque and elevated,
3471
3471
  cells 255 on 242 (light) and 44 on 28 (dark), separators 232 / 64, off tracks 197 / 101. All of
@@ -3510,7 +3510,7 @@ body[data-satin-document='true'],
3510
3510
  }
3511
3511
  /* The sheet's own surface is the backdrop of the glass nested in it (glass/presentationFrame.ts):
3512
3512
  the opaque large-detent tone above (242 light, 28 dark). The renderer has no glass-on-glass
3513
- source, so nested glass on a medium (Liquid Glass) sheet samples this tone too. */
3513
+ source, so nested glass on a medium (glass) sheet samples this tone too. */
3514
3514
  [data-satin-presentation='sheet'] {
3515
3515
  --satin-presentation-backdrop: rgb(242 242 247);
3516
3516
  }
@@ -3643,11 +3643,11 @@ body[data-satin-document='true'],
3643
3643
  border-radius: inherit;
3644
3644
  }
3645
3645
  /* The shared row box above clears row fills; a ListRow keeps its cell or listRowBackground, as a
3646
- SwiftUI row does in every list style (reference rows pair). */
3646
+ row does in every list style (reference rows pair). */
3647
3647
  [data-satin-list-row-options]:not(:has([data-satin-row], [data-satin-field-style='row'])) {
3648
3648
  background: var(--satin-row-background, var(--satin-list-cell, #fff));
3649
3649
  }
3650
- /* Explicit insets size the row to its content: SwiftUI's defaultMinListRowHeight (44 pt) bounds the
3650
+ /* Explicit insets size the row to its content: the minimum row height of 44 pt bounds the
3651
3651
  content inside the insets. iOS 27 native reference rows: a 36 pt capsule with 4 pt insets is a 52 pt row
3652
3652
  (capsule centred, 8 pt above and below); with zero insets it is 44 pt (reference search rows). */
3653
3653
  [data-satin-list-row-options][data-satin-row-insets='explicit']:not(
@@ -3730,6 +3730,12 @@ body[data-satin-document='true'],
3730
3730
  flex: 1;
3731
3731
  overflow-wrap: anywhere;
3732
3732
  }
3733
+ /* The sheet Form reference measures plain-text Stepper labels only: 72 1/3 and
3734
+ 94 1/3 pt for two/three lines. Do not apply its extra inset to composed List
3735
+ labels or RowLabel stacks; those retain their independently measured pitch. */
3736
+ .satin-stepper-row > [data-satin-row-label] {
3737
+ padding-block: 10.166667px;
3738
+ }
3733
3739
  [data-satin-row-trailing='true'] {
3734
3740
  display: flex;
3735
3741
  align-items: center;
@@ -4008,11 +4014,15 @@ body[data-satin-document='true'],
4008
4014
  inset: 6px 0;
4009
4015
  z-index: -1;
4010
4016
  border-radius: 18px;
4011
- background: var(--satin-list-stepper, #eeeef0);
4017
+ background: var(--satin-list-stepper, #eeeeef);
4012
4018
  pointer-events: none;
4013
4019
  }
4014
4020
  [data-satin-stepper='true'] button {
4015
4021
  position: relative;
4022
+ display: flex;
4023
+ align-items: center;
4024
+ justify-content: center;
4025
+ text-align: center;
4016
4026
  width: 50%;
4017
4027
  min-width: 0;
4018
4028
  min-height: 44px;
@@ -4037,7 +4047,8 @@ body[data-satin-document='true'],
4037
4047
  top: 10px;
4038
4048
  bottom: 10px;
4039
4049
  width: 1px;
4040
- background: var(--satin-list-separator, #c6c6c8);
4050
+ /* Native divider uses label at 30% over the control, independent of list separators. */
4051
+ background: var(--satin-placeholder-text);
4041
4052
  pointer-events: none;
4042
4053
  }
4043
4054
  [data-satin-stepper='true'] button:disabled {
@@ -4146,7 +4157,7 @@ body[data-satin-document='true'],
4146
4157
  --satin-list-menu: #2c2c2e;
4147
4158
  --satin-list-toggle-off: #5a5a5e;
4148
4159
  --satin-list-toggle-on: #30d158;
4149
- --satin-list-stepper: #2c2c2e;
4160
+ --satin-list-stepper: #3b3b3d;
4150
4161
  --satin-list-segment-selected: #636366;
4151
4162
  }
4152
4163
 
@@ -4323,6 +4334,16 @@ body[data-satin-document='true'],
4323
4334
  }
4324
4335
  /* A Form editor's first line sits 23 pt below the cell top and its text 5 pt inside the row
4325
4336
  inset (UITextView line-fragment padding); each further line adds 22 pt. Stock iOS 27 pairs. */
4337
+ /* Form controls share the 20 pt content inset, including multiline editors. */
4338
+ .satin-text-editor[data-satin-field-style='row'] {
4339
+ padding-inline: 20px;
4340
+ }
4341
+ /* A vertical TextField uses the stock multiline row pitch (22 pt per line + 28 1/3 pt).
4342
+ TextEditor retains its separate UITextView content padding below. */
4343
+ .satin-text-editor[data-satin-field-style='row']:has(> [data-satin-field-axis='vertical']) {
4344
+ min-height: 52px;
4345
+ padding-block: 14.166667px;
4346
+ }
4326
4347
  [data-satin-field-style='row'] > [data-satin-text-editor] {
4327
4348
  padding: 12px 5px;
4328
4349
  }
@@ -4868,7 +4889,7 @@ html[data-satin-appearance='dark'] .satin-sheet-grabber > span {
4868
4889
  );
4869
4890
  }
4870
4891
  /* reference: the screen owns scrolling geometry. A screen's primary scroll view (ScrollSurface, elected
4871
- like SwiftUI's: the outermost vertical scroll view that starts the screen's content) extends under
4892
+ the outermost vertical scroll view that starts the screen's content) extends under
4872
4893
  the bar and takes the bar's content inset and large title. Native iOS 27, iPhone 17 Pro Max:
4873
4894
  inline safe top 54 pt below the status band, large 106 pt (54 + a 52 pt title row). */
4874
4895
  :where([data-satin-scroll-navigation]) {
@@ -4928,7 +4949,7 @@ html[data-satin-appearance='dark'] .satin-sheet-grabber > span {
4928
4949
  [data-satin-navigation-chrome][data-satin-navigation-bar-hidden='true'] * {
4929
4950
  pointer-events: none;
4930
4951
  }
4931
- /* inlineLarge (SwiftUI toolbarTitleDisplayMode): the large title stands in the bar row itself
4952
+ /* inlineLarge title layout: the large title stands in the bar row itself
4932
4953
  (native reference header-styles r1: frame x20 y63.667 h40.667, 34 pt bold, the owner's Apple Music rest
4933
4954
  pixels) and gives way to the inline title within the first 5 pt of scroll (NavigationChrome). */
4934
4955
  [data-satin-navigation-title-display='inlineLarge'] [data-satin-navigation-title-content] > strong {
@@ -4971,7 +4992,7 @@ html[data-satin-appearance='dark'] .satin-sheet-grabber > span {
4971
4992
  letter-spacing: 0;
4972
4993
  }
4973
4994
  /* Without a primary scroll view the screen's content sits in the safe area below the bar, as a
4974
- SwiftUI VStack does; a large title stands in the bar's title row. */
4995
+ VStack does; a large title stands in the bar's title row. */
4975
4996
  [data-satin-navigation-motion-layer='true'] {
4976
4997
  box-sizing: border-box;
4977
4998
  }
@@ -5101,7 +5122,7 @@ html[data-satin-appearance='dark'] [data-satin-scroll-indicator-thumb='true'] {
5101
5122
  letter-spacing: 0;
5102
5123
  }
5103
5124
 
5104
- /* Stock SwiftUI iOS 27 Form, same-content rest/scrolled fit, DPR 3.
5125
+ /* Native controls iOS 27 Form, same-content rest/scrolled fit, DPR 3.
5105
5126
  Source and residuals: measured-reference-full-bleed-notes.md. These are material
5106
5127
  measurements, not spring values; installed cross-content parity is separate. */
5107
5128
  /* Separate stacking contexts keep WebKit backdrop sampling below both the
@@ -5656,7 +5677,7 @@ html[data-satin-appearance='dark'] [data-satin-navigation-chrome] {
5656
5677
  position: relative;
5657
5678
  top: calc(0.333333px * var(--satin-text-scale, 1));
5658
5679
  }
5659
- /* Stock SwiftUI List allocates a 64 pt row around the 34 pt bordered control. */
5680
+ /* Native controls List allocates a 64 pt row around the 34 pt bordered control. */
5660
5681
  [data-satin-section-item]:has(> [data-satin-button-style='borderedProminent']),
5661
5682
  [data-satin-section-item]:has(> [data-satin-button-style='bordered']),
5662
5683
  [data-satin-section-item]:has(
@@ -5853,7 +5874,7 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
5853
5874
  }
5854
5875
  /* A plain glyph (a Menu labelled with a bare symbol) fades back in as its menu's drop dissolves
5855
5876
  into it, measured from the close: hidden to +240 ms, 0.3 at +256, 0.62 at +290, 0.85 at
5856
- +325 and whole by +400 ms (stock SwiftUI, iOS 27; menuGlyphDropFit.json). The animation
5877
+ +325 and whole by +400 ms (native controls, iOS 27; menuGlyphDropFit.json). The animation
5857
5878
  outranks the emerged state's opacity while the drop is still dissolving. */
5858
5879
  [data-satin-menu-glyph-return='true'] {
5859
5880
  animation: satin-menu-glyph-return 400ms linear both;
@@ -5987,7 +6008,7 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
5987
6008
  [data-satin-row]:has([data-satin-row-label-stack]) {
5988
6009
  padding-block: 5px;
5989
6010
  }
5990
- /* SwiftUI LabeledContent moves its value below the label when their intrinsic
6011
+ /* LabeledContent moves its value below the label when their intrinsic
5991
6012
  widths do not fit. Growing the label fills the first line; an unshrunk label
5992
6013
  makes flex wrapping move the value to the leading edge of the next line. */
5993
6014
  [data-satin-row-value-content] {
@@ -6024,13 +6045,15 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
6024
6045
  padding-block: 14.166667px;
6025
6046
  }
6026
6047
 
6027
- /* Layout frames, rather than glyph ink bounds, match SwiftUI Text exports. */
6048
+ /* Text measurement uses layout frames rather than glyph ink bounds. */
6028
6049
  [data-satin-row-text] {
6029
6050
  display: inline-block;
6051
+ /* Stock row labels retain authored line breaks, including Stepper labels. */
6052
+ white-space: pre-wrap;
6030
6053
  max-width: 100%;
6031
6054
  vertical-align: top;
6032
6055
  /* Installed 3x reference: the 17 pt WebKit baseline is one device pixel
6033
- * above SwiftUI. The correction stays one device pixel at each supported DPR. */
6056
+ * above the measured baseline. The correction stays one device pixel at each supported DPR. */
6034
6057
  position: relative;
6035
6058
  top: var(--satin-device-pixel, 1px);
6036
6059
  }
@@ -6067,7 +6090,7 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
6067
6090
  line-height: calc(22px * var(--satin-text-scale, 1));
6068
6091
  }
6069
6092
 
6070
- /* SwiftUI SecureField exports a 21 pt editor, while TextField exports 22 pt.
6093
+ /* SecureField has a 21 pt editor, while TextField exports 22 pt.
6071
6094
  * The enclosing native row retains its 52 pt minimum and centers either editor. */
6072
6095
  [data-satin-section-item]
6073
6096
  [data-satin-field-style='row'][data-satin-label-placement='leading']
@@ -6444,8 +6467,8 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
6444
6467
  pointer-events: none;
6445
6468
  }
6446
6469
 
6447
- /* A row's own fill (a ListRow's cell or listRowBackground) follows the card's corners, as a SwiftUI
6448
- row's background does in an inset-grouped section (reference rows pair: square corners otherwise). */
6470
+ /* A row's own fill (a ListRow's cell or listRowBackground) follows the card corners
6471
+ in an inset-grouped section. Other row backgrounds keep square corners. */
6449
6472
  [data-satin-section-rows='true'] > [data-satin-section-item][data-satin-first-row] {
6450
6473
  border-start-start-radius: inherit;
6451
6474
  border-start-end-radius: inherit;
@@ -6610,7 +6633,7 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
6610
6633
  font: inherit;
6611
6634
  line-height: normal;
6612
6635
  }
6613
- /* Stock regular SwiftUI content allocates a 20 1/3 pt line box. */
6636
+ /* Stock regular content allocates a 20 1/3 pt line box. */
6614
6637
  [data-satin-button-style='plain'],
6615
6638
  [data-satin-button-style='borderless'] {
6616
6639
  line-height: calc(20.333333px * var(--satin-text-scale, 1));
@@ -6764,7 +6787,7 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
6764
6787
  --satin-control-held-ink: inherit;
6765
6788
  --satin-control-held-ink-opacity: 0.4;
6766
6789
  }
6767
- /* Omitted SwiftUI tint uses a neutral bordered fill; an explicit/inherited
6790
+ /* Omitted tint uses a neutral bordered fill; an explicit/inherited
6768
6791
  author tint owns the colored recipe above. Toggle-button selection stays tinted. */
6769
6792
  [data-satin-button-style='bordered']:not([data-satin-control-tinted='true']):not(
6770
6793
  [data-satin-toggle-style]
@@ -6918,7 +6941,7 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
6918
6941
  }
6919
6942
 
6920
6943
  /* The bottom safe-area chain (SafeAreaInset, TabView): tab bar, bottom accessory, app insets and
6921
- the keyboard, as in SwiftUI.
6944
+ the keyboard.
6922
6945
  - The root boundary sits above the keyboard. The keyboard reaches it on the keyboard's own
6923
6946
  measured curve (keyboard/motion.ts), started as far into that curve as the system keyboard
6924
6947
  already is when WebKit reports it (--satin-keyboard-phase), so the composer and the thread
@@ -7086,7 +7109,7 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
7086
7109
  --satin-inset-band: calc(var(--satin-safe-top, 0px) + var(--satin-inset-parent-top));
7087
7110
  margin-top: var(--satin-inset-band);
7088
7111
  }
7089
- /* SwiftUI's `background` ignores the safe area: the accessory's root background reaches the
7112
+ /* The accessory background extends beyond its safe area: the accessory's root background reaches the
7090
7113
  screen's top edge, under the bar (`inset-top`: yellow 0–160.33). */
7091
7114
  :where([data-satin-inset-accessory='top'] > *) {
7092
7115
  position: relative;
@@ -7582,9 +7605,7 @@ html[data-satin-appearance='dark'] [data-satin-tab-sidebar-dim] {
7582
7605
  right: 69px;
7583
7606
  }
7584
7607
 
7585
- /* reference searchable: the navigation bar drawer (search/SearchDrawer.tsx). Geometry from the stock
7586
- SwiftUI `.searchable(placement: .navigationBarDrawer(displayMode: .always)).searchScopes`
7587
- reference on iPhone 17 Pro Max, iOS 27.0: at rest the
7608
+ /* reference searchable: the navigation bar drawer (search/SearchDrawer.tsx). Measured on iPhone 17 Pro Max, iOS 27.0: at rest the
7588
7609
  field is [20,168,400,44] under a large title and [20,116,400,44] under an inline one; presented,
7589
7610
  the field is [20,62,345,44], the close button [376,62,44,44] and the scope bar [20,116,400,36].
7590
7611
  The drawer is 54 pt tall (field + 10), 90 pt with a presented scope bar, which ends at 152;
@@ -8409,7 +8430,7 @@ html[data-satin-appearance='dark'] [data-satin-tab-sidebar-dim] {
8409
8430
  display: none;
8410
8431
  }
8411
8432
 
8412
- /* A vertical lazy stack accepts the available cross-axis proposal, as SwiftUI does. */
8433
+ /* A vertical lazy stack accepts the available cross-axis proposal. */
8413
8434
  [data-satin-content='vstack'][data-satin-lazy-stack='vertical'],
8414
8435
  [data-satin-content='frame'] > [data-satin-content='vstack']:has(> [data-satin-content='divider']),
8415
8436
  [data-satin-content='frame']
@@ -8655,7 +8676,7 @@ html[data-satin-appearance='dark'] [data-satin-tab-sidebar-dim] {
8655
8676
  opacity: 0;
8656
8677
  }
8657
8678
 
8658
- /* List edit mode (reference). Native geometry from the stock SwiftUI native reference (`native list editing fixture`, iPhone 17
8679
+ /* List edit mode (reference). Native geometry from the measurement fixture (`native list editing fixture`, iPhone 17
8659
8680
  Pro Max, 3x stills): each leading control sits in a 40 pt column, centred 32 pt (then 72 pt)
8660
8681
  from the row's edge; the 21.33 pt delete circle carries a 10.67 x 1.67 pt bar; the 21.67 pt
8661
8682
  selection circle has a 1.67 pt tertiary-label ring, or the tint with a white check; the 21 x 10.67
@@ -8889,7 +8910,7 @@ html:is([data-satin-viewport-fit='auto'], [data-satin-viewport-fit='contain'])
8889
8910
  height: max(100%, var(--satin-inline-title-content-height, 0px));
8890
8911
  }
8891
8912
 
8892
- /* SwiftUI overlay(alignment:content:): the modified view owns the frame. The overlay consumes
8913
+ /* An overlay leaves its modified view in control of the frame. The overlay consumes
8893
8914
  the inset store directly, including the system extension behind bottom accessories. */
8894
8915
  [data-satin-overlay='true'] {
8895
8916
  position: relative;
@@ -24,7 +24,11 @@ export const TextEditor = forwardRef(function TextEditor({ value, onChange, labe
24
24
  // Only a scroll surface bounds growth. A content-sized parent is the editor's own height,
25
25
  // so reading it as a limit would stop the editor from ever growing.
26
26
  const containerHeight = surface?.clientHeight ?? Infinity;
27
- const visibleHeight = keyboard.viewportHeight > 0
27
+ // A sheet enters from outside the viewport. Its transformed screen position is
28
+ // not an editing limit: transforms do not deliver a ResizeObserver notification
29
+ // when the sheet settles. Only intersect screen geometry for an open keyboard.
30
+ const keyboardVisible = keyboard.height > 0 && keyboard.viewportHeight > 0;
31
+ const visibleHeight = keyboardVisible
28
32
  ? keyboard.viewportHeight -
29
33
  Math.max(0, (surface?.getBoundingClientRect().top ?? 0) - keyboard.offsetTop)
30
34
  : containerHeight;
@@ -32,7 +36,7 @@ export const TextEditor = forwardRef(function TextEditor({ value, onChange, labe
32
36
  // to measure it: use an isolated, non-focusable copy with the same text layout instead.
33
37
  const available = maxLines === undefined
34
38
  ? Math.min(containerHeight, visibleHeight) - 24
35
- : Math.min(Math.max(minLines, maxLines) * lineHeight + padding, keyboard.viewportHeight > 0 ? visibleHeight - 24 : Infinity);
39
+ : Math.min(Math.max(minLines, maxLines) * lineHeight + padding, keyboardVisible ? visibleHeight - 24 : Infinity);
36
40
  const height = editorHeight(measureEditorContentHeight(element), minLines, lineHeight, padding, available);
37
41
  if (element.style.height === `${height}px`)
38
42
  return;
@@ -65,7 +69,7 @@ export const TextEditor = forwardRef(function TextEditor({ value, onChange, labe
65
69
  observer.disconnect();
66
70
  element.removeEventListener('input', resize);
67
71
  };
68
- }, [value, minLines, maxLines, keyboard.viewportHeight, keyboard.offsetTop]);
72
+ }, [value, minLines, maxLines, keyboard.height, keyboard.viewportHeight, keyboard.offsetTop]);
69
73
  useLayoutEffect(() => {
70
74
  if (autoFocus && ref.current)
71
75
  focusFromFramework(ref.current, { preventScroll: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@satinhq/kit",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.3",
4
4
  "type": "module",
5
5
  "description": "Opinionated single-target framework for agent-generated personal iOS Home Screen apps.",
6
6
  "main": "./dist/index.js",
@@ -96,7 +96,7 @@
96
96
  "repository": {
97
97
  "type": "git",
98
98
  "url": "git+https://github.com/satinhq/satin.git",
99
- "directory": "packages/pwakit"
99
+ "directory": "packages/kit"
100
100
  },
101
101
  "engines": {
102
102
  "node": "^22.12.0 || >=24.0.0"
@@ -111,8 +111,8 @@
111
111
  "scripts": {
112
112
  "build": "node scripts/build.mjs",
113
113
  "typecheck": "tsc -p tsconfig.json --pretty false",
114
- "lint": "oxlint && node ../pwakit-lint/scripts/check-webgpu-worker.mjs src/glass/webgpu/entry.worker.ts && node ../pwakit-lint/scripts/check-sidebar-artwork.mjs src/navigation/SidebarSymbol.tsx && node ../pwakit-lint/scripts/check-modal-lifecycle.mjs src",
115
- "test": "vitest run && node scripts/check-readme-examples.mjs && node scripts/check-public-exports.mjs && node scripts/api-report.mjs && node --test scripts/api-graph.node-test.mjs scripts/public-types.node-test.mjs scripts/swiftui-reference.node-test.mjs scripts/path-spaces.node-test.mjs",
114
+ "lint": "oxlint && node ../lint/scripts/check-webgpu-worker.mjs src/glass/webgpu/entry.worker.ts && node ../lint/scripts/check-sidebar-artwork.mjs src/navigation/SidebarSymbol.tsx && node ../lint/scripts/check-modal-lifecycle.mjs src",
115
+ "test": "vitest run && node scripts/check-readme-examples.mjs && node scripts/check-public-exports.mjs && node scripts/api-report.mjs && node --test scripts/api-graph.node-test.mjs scripts/public-types.node-test.mjs scripts/api-reference.node-test.mjs scripts/path-spaces.node-test.mjs",
116
116
  "api:write": "node scripts/api-report.mjs --write",
117
117
  "api:check": "node scripts/api-report.mjs"
118
118
  }