@satinhq/kit 0.1.0-beta.1 → 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.
- package/NOTICE +2 -2
- package/README.md +1 -1
- package/dist/acknowledgements/inventory.json +5 -5
- package/dist/actions/RowActions.d.ts +2 -2
- package/dist/actions/RowActions.js +2 -2
- package/dist/app/deployment.js +8 -2
- package/dist/app/installCheck.js +4 -3
- package/dist/app/launchWatchdog.js +6 -1
- package/dist/controls/civilDate.d.ts +1 -1
- package/dist/controls/civilDate.js +1 -1
- package/dist/docs/components/file-importer.md +1 -1
- package/dist/docs/components/paste-button.md +1 -1
- package/dist/docs/components/photos-picker.md +1 -1
- package/dist/docs/components/share-link.md +1 -1
- package/dist/docs/limitations.md +1 -1
- package/dist/docs/push.md +3 -3
- package/dist/docs/reference.md +1870 -0
- package/dist/glass/GlassMaterialRoot.d.ts +3 -1
- package/dist/glass/GlassMaterialRoot.js +2 -2
- package/dist/glass/MaterialEffectRoot.js +5 -1
- package/dist/glass/errors.d.ts +3 -1
- package/dist/glass/errors.js +49 -3
- package/dist/glass/fallback/canvasOwnership.d.ts +7 -0
- package/dist/glass/fallback/canvasOwnership.js +23 -0
- package/dist/glass/fallback/material.js +3 -2
- package/dist/glass/fallback/regions.d.ts +1 -0
- package/dist/glass/fallback/regions.js +9 -12
- package/dist/glass/fallback/runtime.d.ts +2 -0
- package/dist/glass/fallback/runtime.js +15 -4
- package/dist/glass/lens/controlLift.d.ts +1 -1
- package/dist/glass/lens/controlLift.js +1 -1
- package/dist/glass/lens/interactivePress.d.ts +2 -2
- package/dist/glass/lens/interactivePress.js +2 -2
- package/dist/glass/lens/menu-opening-native-fits.json +1 -1
- package/dist/glass/lens/menuContextFit.json +1 -1
- package/dist/glass/lens/menuDropFit.json +1 -1
- package/dist/glass/lens/menuField.d.ts +2 -2
- package/dist/glass/lens/menuField.js +3 -3
- package/dist/glass/lens/menuGlyphDropFit.json +1 -1
- package/dist/glass/lens/menuMorphFit.json +1 -1
- package/dist/glass/lens/menuMorphPhoneFit.json +1 -1
- package/dist/glass/lens/tabAccessoryMotion.d.ts +1 -1
- package/dist/glass/lens/tabSearchMember.d.ts +1 -1
- package/dist/glass/lens/tabSearchMember.js +1 -1
- package/dist/glass/menuPresenter.js +1 -1
- package/dist/glass/regions.d.ts +1 -1
- package/dist/glass/regions.js +1 -1
- package/dist/glass/shapeTrack.d.ts +1 -1
- package/dist/glass/submenuMotion.d.ts +1 -1
- package/dist/glass/submenuMotion.js +1 -1
- package/dist/glass/webgpu/client.d.ts +3 -0
- package/dist/glass/webgpu/client.js +57 -9
- package/dist/glass/webgpu/entry.worker.js +3 -6
- package/dist/glass/webgpu/errorCode.js +3 -2
- package/dist/glass/webgpu/frameClock.d.ts +6 -2
- package/dist/glass/webgpu/frameClock.js +9 -2
- package/dist/glass/webgpu/frameProbe.d.ts +1 -1
- package/dist/glass/webgpu/frameProbe.js +3 -3
- package/dist/glass/webgpu/layers.d.ts +5 -1
- package/dist/glass/webgpu/layers.js +61 -6
- package/dist/glass/webgpu/minimize.js +20 -2
- package/dist/glass/webgpu/production.js +13 -1
- package/dist/glass/webgpu/raster.d.ts +18 -0
- package/dist/glass/webgpu/raster.js +105 -15
- package/dist/glass/webgpu/rasterMaterial.d.ts +4 -0
- package/dist/glass/webgpu/rasterMaterial.js +17 -1
- package/dist/glass/webgpu/regionScene.d.ts +4 -1
- package/dist/glass/webgpu/regionScene.js +30 -1
- package/dist/glass/webgpu/renderer.js +23 -7
- package/dist/glass/webgpu/sourcePixels.d.ts +4 -4
- package/dist/glass/webgpu/sourcePixels.js +48 -8
- package/dist/keyboard/adapter.js +2 -2
- package/dist/keyboard/avoidance.d.ts +1 -1
- package/dist/keyboard/avoidance.js +1 -1
- package/dist/keyboard/motion.d.ts +1 -1
- package/dist/keyboard/motion.js +1 -1
- package/dist/list-form/ListForm.d.ts +2 -2
- package/dist/list-form/ListForm.js +1 -1
- package/dist/list-form/ListItem.js +1 -1
- package/dist/list-form/RowLabel.d.ts +1 -1
- package/dist/list-form/RowLabel.js +1 -1
- package/dist/list-form/Slider.js +1 -1
- package/dist/list-form/editMotion.js +1 -1
- package/dist/list-form/editing.d.ts +5 -5
- package/dist/list-form/editing.js +1 -1
- package/dist/motion/animationValue.d.ts +2 -2
- package/dist/motion/animationValue.js +9 -10
- package/dist/motion/matchedGeometry.d.ts +4 -4
- package/dist/motion/matchedGeometry.js +7 -8
- package/dist/motion/springTrack.d.ts +1 -2
- package/dist/motion/springTrack.js +10 -5
- package/dist/navigation/ScreenContainer.js +1 -1
- package/dist/navigation/chromeCurves.js +1 -1
- package/dist/navigation/types.d.ts +1 -1
- package/dist/navigation/zoomGeometry.js +1 -1
- package/dist/navigation/zoomMotion.d.ts +1 -1
- package/dist/navigation/zoomMotion.js +2 -2
- package/dist/navigation/zoomTransition.js +3 -4
- package/dist/presentation/Presentation.d.ts +2 -2
- package/dist/presentation/Presentation.js +1 -1
- package/dist/presentation/presentationSource.js +1 -1
- package/dist/public/controls.d.ts +4 -4
- package/dist/public/controls.js +7 -7
- package/dist/public/glass.d.ts +3 -4
- package/dist/public/glass.js +1 -2
- package/dist/public/navigationTransition.d.ts +1 -1
- package/dist/public/navigationTransition.js +1 -1
- package/dist/public/scroll.d.ts +1 -1
- package/dist/public/scroll.js +1 -1
- package/dist/public/toolbar.js +1 -1
- package/dist/root/SatinRoot.js +9 -5
- package/dist/safe-area/SafeAreaInset.js +3 -3
- package/dist/safe-area/modifiers.js +2 -2
- package/dist/safe-area/useSafeAreaMetrics.js +2 -2
- package/dist/scroll/ScrollSurface.d.ts +5 -5
- package/dist/scroll/ScrollSurface.js +1 -1
- package/dist/scroll/position.js +1 -1
- package/dist/scroll/screenGeometry.d.ts +1 -1
- package/dist/scroll/screenGeometry.js +1 -1
- package/dist/search/SearchDrawer.js +1 -1
- package/dist/search/searchMotion.d.ts +1 -1
- package/dist/search/searchMotion.js +1 -1
- package/dist/search/useSearchPresentation.js +1 -1
- package/dist/shapes/corners.js +1 -1
- package/dist/styles.css +55 -34
- package/dist/sw/renderServiceWorker.js +12 -3
- package/dist/text-input/TextEditor.js +7 -3
- package/package.json +4 -4
- package/dist/docs/swiftui-reference.md +0 -1895
|
@@ -6,7 +6,7 @@ import { IgnoresSafeAreaContext } from './context.js';
|
|
|
6
6
|
import { createContext, useContext, useLayoutEffect, useRef, useMemo, useCallback, } from 'react';
|
|
7
7
|
/** Whether an enclosing SafeAreaInset already owns the bottom boundary (keyboard, home indicator). */
|
|
8
8
|
export const BottomInsetContext = createContext({ consumed: false });
|
|
9
|
-
/**
|
|
9
|
+
/** Default spacing between content and a safe-area inset. */
|
|
10
10
|
const SYSTEM_INSET_SPACING_PX = 8;
|
|
11
11
|
const ACCESSORY_INSET_SCOPE = { consumed: true };
|
|
12
12
|
/**
|
|
@@ -41,7 +41,7 @@ function BottomInset({ content, spacing, children, ...props }) {
|
|
|
41
41
|
geometry.publish((parent.geometry?.value ?? 0) + measuredHeight.current + spacing + extension);
|
|
42
42
|
}, [geometry, parent.geometry, spacing]);
|
|
43
43
|
// `IgnoresSafeArea regions="keyboard"` around the inset: the boundary stays above the home
|
|
44
|
-
// indicator while the keyboard is up
|
|
44
|
+
// indicator while the keyboard is up `ignoresSafeArea(.keyboard)`.
|
|
45
45
|
const keyboardIgnored = useContext(IgnoresSafeAreaContext).keyboard;
|
|
46
46
|
const inset = useRef(null);
|
|
47
47
|
const accessory = useRef(null);
|
|
@@ -112,7 +112,7 @@ function BottomInset({ content, spacing, children, ...props }) {
|
|
|
112
112
|
* native adds it to the scroll view's adjusted inset (iOS 27 native reference `inset-top`: 116 + 44.33 + 8);
|
|
113
113
|
* - leading/trailing: the accessory is a column of the safe area's height and the content's frame
|
|
114
114
|
* gives up its width plus `spacing` (`inset-leading`: strip 0–60, cards from 68).
|
|
115
|
-
* A background drawn by the accessory's root view reaches the screen's top edge
|
|
115
|
+
* A background drawn by the accessory's root view reaches the screen's top edge. An accessory
|
|
116
116
|
* `background` ignores the safe area by default (`inset-top`: the yellow fills 0–160.33).
|
|
117
117
|
*/
|
|
118
118
|
function EdgeInset({ edge, content, spacing, children, ...props }) {
|
|
@@ -3,7 +3,7 @@ import { startsScreenContent } from '../scroll/screenGeometry.js';
|
|
|
3
3
|
import { SafeAreaInset } from './SafeAreaInset.js';
|
|
4
4
|
import { ContentMarginsContext, IgnoresSafeAreaContext, NO_LENGTHS, SafeAreaPaddingContext, } from './context.js';
|
|
5
5
|
import { useContext, useLayoutEffect, useRef } from 'react';
|
|
6
|
-
/**
|
|
6
|
+
/** Default padding used when no length is supplied. */
|
|
7
7
|
const SYSTEM_PADDING_PX = 16;
|
|
8
8
|
function edgesOf(set) {
|
|
9
9
|
const edges = new Set();
|
|
@@ -68,7 +68,7 @@ export function IgnoresSafeArea({ regions = 'all', edges = 'all', children, ...p
|
|
|
68
68
|
const container = regions === 'keyboard' ? new Set() : set;
|
|
69
69
|
const keyboard = regions !== 'container' && set.has('bottom');
|
|
70
70
|
const element = useRef(null);
|
|
71
|
-
// Only a view that reaches the safe edge extends past it
|
|
71
|
+
// Only a view that reaches the safe edge extends past it.
|
|
72
72
|
useLayoutEffect(() => {
|
|
73
73
|
const node = element.current;
|
|
74
74
|
if (!node)
|
|
@@ -60,7 +60,7 @@ function readInsets(element) {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
// Native home-indicator boundaries, in points. Phone: Foundation 00, iPhone 17 Pro Max,
|
|
63
|
-
// iOS 27 portrait. Pad: a
|
|
63
|
+
// iOS 27 portrait. Pad: a .safeAreaInset(edge: .bottom) composer on iPad Pro
|
|
64
64
|
// 12.9-inch, iPadOS 27, which keeps 25 pt in portrait and in landscape.
|
|
65
65
|
const PHONE_HOME_INDICATOR_INSET = 34;
|
|
66
66
|
const PAD_HOME_INDICATOR_INSET = 25;
|
|
@@ -72,7 +72,7 @@ const PAD_FITTED_STATUS_BAND = 32;
|
|
|
72
72
|
* With viewport-fit auto/contain, the system takes the status band outside the viewport,
|
|
73
73
|
* but content still reaches the screen bottom and every env() inset reads zero. Without
|
|
74
74
|
* this boundary, bottom bars and composers sit in the home-indicator band: on iPad a
|
|
75
|
-
* composer sat 25 pt lower than the same
|
|
75
|
+
* composer sat 25 pt lower than the same layout, its trailing edge in the corner
|
|
76
76
|
* that carries the window-resize grabber. Device identity comes from the shared
|
|
77
77
|
* resolver, never from screen dimensions.
|
|
78
78
|
*/
|
|
@@ -28,15 +28,15 @@ export type ScrollSurfaceProps = Omit<HTMLAttributes<HTMLDivElement>, 'id'> & Sc
|
|
|
28
28
|
/**
|
|
29
29
|
* Whether this is the screen's primary scroll view, which gets the large title, the bar's
|
|
30
30
|
* content inset and the scroll edge effect. By default the outermost vertical scroll view at the
|
|
31
|
-
* top of the screen's content is primary
|
|
31
|
+
* top of the screen's content is primary.
|
|
32
32
|
*/
|
|
33
33
|
primary?: boolean;
|
|
34
|
-
/**
|
|
34
|
+
/** Hide the scroll edge effect under the top bar. */
|
|
35
35
|
scrollEdgeEffectHidden?: ScrollEdgeEffectHidden;
|
|
36
36
|
/** Soft/automatic select the existing top treatment. Hard is explicitly unsupported. */
|
|
37
37
|
scrollEdgeEffectStyle?: ScrollEdgeEffectStyleSelection;
|
|
38
38
|
}>;
|
|
39
|
-
/**
|
|
39
|
+
/** A named scroll surface whose position is retained by the framework. */
|
|
40
40
|
export declare const ScrollSurface: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "id"> & Readonly<{
|
|
41
41
|
defaultScrollAnchor?: 'top' | 'bottom';
|
|
42
42
|
maintainVisibleContentPosition?: boolean;
|
|
@@ -52,10 +52,10 @@ export declare const ScrollSurface: import("react").ForwardRefExoticComponent<Om
|
|
|
52
52
|
/**
|
|
53
53
|
* Whether this is the screen's primary scroll view, which gets the large title, the bar's
|
|
54
54
|
* content inset and the scroll edge effect. By default the outermost vertical scroll view at the
|
|
55
|
-
* top of the screen's content is primary
|
|
55
|
+
* top of the screen's content is primary.
|
|
56
56
|
*/
|
|
57
57
|
primary?: boolean;
|
|
58
|
-
/**
|
|
58
|
+
/** Hide the scroll edge effect under the top bar. */
|
|
59
59
|
scrollEdgeEffectHidden?: ScrollEdgeEffectHidden;
|
|
60
60
|
/** Soft/automatic select the existing top treatment. Hard is explicitly unsupported. */
|
|
61
61
|
scrollEdgeEffectStyle?: ScrollEdgeEffectStyleSelection;
|
|
@@ -27,7 +27,7 @@ export const ListNavigationContext = createContext(undefined);
|
|
|
27
27
|
/** A scroll view's content is inside its safe area: modifiers above it stop at its frame. */
|
|
28
28
|
const NO_IGNORED = { container: new Set(), keyboard: false };
|
|
29
29
|
const cssLength = (value) => typeof value === 'number' ? `${value}px` : (value ?? '0px');
|
|
30
|
-
/**
|
|
30
|
+
/** A named scroll surface whose position is retained by the framework. */
|
|
31
31
|
export const ScrollSurface = forwardRef(function ScrollSurface({ axis = 'vertical', chain = false, children, primary, scrollEdgeEffectHidden, scrollEdgeEffectStyle, refreshable, scrollDismissesKeyboard = 'never', id, defaultScrollAnchor, maintainVisibleContentPosition, scrollPosition, onScrollGeometryChange, ...props }, forwardedRef) {
|
|
32
32
|
const refresh = refreshable;
|
|
33
33
|
if (axis !== 'vertical' &&
|
package/dist/scroll/position.js
CHANGED
|
@@ -84,7 +84,7 @@ export function createScrollPositionController(surface, content, targets, option
|
|
|
84
84
|
pending = undefined;
|
|
85
85
|
const targetTop = top(target);
|
|
86
86
|
const height = target.getBoundingClientRect().height;
|
|
87
|
-
// Targets align within the content insets,
|
|
87
|
+
// Targets align within the content insets, within the owned scroll region: a screen's primary scroll view
|
|
88
88
|
// reaches under the bar and the bottom bars, and its padding is the bar's and the bottom inset
|
|
89
89
|
// chain's content inset. Other scroll views only clear the enclosing bottom insets' accessories.
|
|
90
90
|
const style = getComputedStyle(surface);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Whether a scroll view starts its screen's content, as a
|
|
2
|
+
* Whether a scroll view starts its screen's content, as a scroll view that reaches the
|
|
3
3
|
* top safe-area edge does. Only such a scroll view extends under the navigation bar and takes the
|
|
4
4
|
* bar's content inset; one placed below other content keeps its own frame, like a Form under a
|
|
5
5
|
* header in a VStack.
|
|
@@ -11,7 +11,7 @@ function occupiesFlow(element) {
|
|
|
11
11
|
return position !== 'absolute' && position !== 'fixed';
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Whether a scroll view starts its screen's content, as a
|
|
14
|
+
* Whether a scroll view starts its screen's content, as a scroll view that reaches the
|
|
15
15
|
* top safe-area edge does. Only such a scroll view extends under the navigation bar and takes the
|
|
16
16
|
* bar's content inset; one placed below other content keeps its own frame, like a Form under a
|
|
17
17
|
* header in a VStack.
|
|
@@ -258,7 +258,7 @@ function pinProgress(drawer) {
|
|
|
258
258
|
}
|
|
259
259
|
/* How far the resting field has turned from its grey fill to glass. The field is glass from
|
|
260
260
|
* scroll 0 only when the drawer is fixed in the bar: an explicit `navigationBarDrawer` placement
|
|
261
|
-
*
|
|
261
|
+
* with persistent visibility under an inline or inlineLarge title (native reference reference and reference
|
|
262
262
|
* B/H, iOS 27.0: light 251 with a darker rim, dark 32 with a light rim). Under a large title the
|
|
263
263
|
* field belongs to the content header, and an automatic placement's drawer scrolls away with the
|
|
264
264
|
* content; both are a grey fill until content scrolls under the field (reference A/C/D/G: light 228-239,
|
|
@@ -3,7 +3,7 @@ import { sampledKeyframes, springTrack, type SpringParameters, type SpringTrack
|
|
|
3
3
|
export { sampledKeyframes, springTrack, type SpringTrack };
|
|
4
4
|
/**
|
|
5
5
|
* Native `.searchable` presentation and dismissal, measured from the presentation layer of a
|
|
6
|
-
*
|
|
6
|
+
* native search drawer (iOS 27.0, 15 focus/close trials in light and dark,
|
|
7
7
|
* every frame).
|
|
8
8
|
*
|
|
9
9
|
* - The field's position and width, the list content, the close button and the field's grey fill
|
|
@@ -3,7 +3,7 @@ import { sampledKeyframes, springMotion, springTrack, } from '../motion/springTr
|
|
|
3
3
|
export { sampledKeyframes, springTrack };
|
|
4
4
|
/**
|
|
5
5
|
* Native `.searchable` presentation and dismissal, measured from the presentation layer of a
|
|
6
|
-
*
|
|
6
|
+
* native search drawer (iOS 27.0, 15 focus/close trials in light and dark,
|
|
7
7
|
* every frame).
|
|
8
8
|
*
|
|
9
9
|
* - The field's position and width, the list content, the close button and the field's grey fill
|
|
@@ -3,7 +3,7 @@ import { focusFromFramework } from '../focus/programmaticFocus.js';
|
|
|
3
3
|
import { validateSearchPresentation } from './searchable.js';
|
|
4
4
|
/*
|
|
5
5
|
* The state of one `searchable` field, whatever draws it: presentation (owned or controlled by
|
|
6
|
-
* `isPresented:`), text changes, submission and suggestion completion
|
|
6
|
+
* `isPresented:`), text changes, submission and suggestion completion search
|
|
7
7
|
* controller. The bottom toolbar field uses it (SearchToolbarField.tsx); the navigation bar drawer
|
|
8
8
|
* keeps its own copy until reference adopts this hook (spec notes, reference construction update).
|
|
9
9
|
*/
|
package/dist/shapes/corners.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Continuous rounded corners measured on iOS 27.0. The
|
|
2
2
|
* straight-edge approach contracts independently on each axis for short boxes;
|
|
3
3
|
* clamping the entire continuous corner to half-height changes its curvature. */
|
|
4
4
|
const extent = 1.5286649466;
|
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
|
-
/*
|
|
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
|
-
/*
|
|
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
|
-
/*
|
|
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
|
|
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,
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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:
|
|
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, #
|
|
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
|
-
|
|
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: #
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
/*
|
|
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
|
-
/*
|
|
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 (
|
|
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
|
-
/*
|
|
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
|
-
/*
|
|
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
|
|
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
|
-
/*
|
|
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
|
|
6448
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
/*
|
|
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).
|
|
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
|
|
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
|
|
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
|
-
/*
|
|
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;
|
|
@@ -39,7 +39,8 @@ self.addEventListener('install', (event) => {
|
|
|
39
39
|
await reportProgress()
|
|
40
40
|
const activeDownloads = new Set()
|
|
41
41
|
let failureReport
|
|
42
|
-
const download = async (entry) => {
|
|
42
|
+
const download = async (entry, attempt = 0) => {
|
|
43
|
+
let received = 0
|
|
43
44
|
let networkFailure = true
|
|
44
45
|
let stalled = false
|
|
45
46
|
const controller = new AbortController()
|
|
@@ -76,7 +77,7 @@ self.addEventListener('install', (event) => {
|
|
|
76
77
|
progress.activity++
|
|
77
78
|
await reportProgress()
|
|
78
79
|
if (installFailure) throw new Error('Installation cancelled')
|
|
79
|
-
networkFailure =
|
|
80
|
+
networkFailure = response.status >= 500 || response.status === 408 || response.status === 429
|
|
80
81
|
if (!response.ok || response.type === 'opaque') throw new Error('HTTP ' + response.status)
|
|
81
82
|
if (response.redirected) throw new Error('Precache resource redirected')
|
|
82
83
|
const mime = (response.headers.get('content-type') || '').split(';')[0].trim().toLowerCase()
|
|
@@ -86,7 +87,6 @@ self.addEventListener('install', (event) => {
|
|
|
86
87
|
networkFailure = true
|
|
87
88
|
reader = response.body?.getReader()
|
|
88
89
|
const chunks = []
|
|
89
|
-
let received = 0
|
|
90
90
|
if (reader) {
|
|
91
91
|
while (true) {
|
|
92
92
|
const { done, value } = await Promise.race([reader.read(), idleFailure])
|
|
@@ -112,6 +112,15 @@ self.addEventListener('install', (event) => {
|
|
|
112
112
|
if (installFailure) throw new Error('Installation cancelled')
|
|
113
113
|
await cache.put(request, new Response(blob, { status: response.status, statusText: response.statusText, headers: response.headers }))
|
|
114
114
|
} catch (error) {
|
|
115
|
+
if (!installFailure && (networkFailure || stalled) && attempt < 2) {
|
|
116
|
+
clearTimeout(idleTimer)
|
|
117
|
+
activeDownloads.delete(cancelDownload)
|
|
118
|
+
// Retransmitted bytes are not extra installed bytes.
|
|
119
|
+
progress.loaded -= received
|
|
120
|
+
await new Promise(resolve => setTimeout(resolve, 250 * 2 ** attempt))
|
|
121
|
+
if (installFailure) throw error
|
|
122
|
+
return download(entry, attempt + 1)
|
|
123
|
+
}
|
|
115
124
|
if (!installFailure) {
|
|
116
125
|
installFailure = { type: 'SATIN_INSTALL_FAILURE', buildId: BUILD_ID, url: entry.url,
|
|
117
126
|
reason: error instanceof Error ? error.message : String(error), networkFailure, stalled }
|
|
@@ -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
|
-
|
|
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,
|
|
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.
|
|
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/
|
|
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 ../
|
|
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/
|
|
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
|
}
|