@graineai/inapp-react-native 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INTEGRATION.md +2 -2
- package/dist/client.d.ts +2 -0
- package/dist/design.d.ts +5 -0
- package/dist/design.js +50 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/react-native/design-math.d.ts +13 -0
- package/dist/react-native/design-math.js +85 -0
- package/dist/react-native/fab.js +57 -5
- package/dist/react-native/index.d.ts +6 -1
- package/dist/react-native/index.js +18 -2
- package/dist/react-native/loader.js +23 -7
- package/dist/react-native/ui.js +139 -6
- package/dist/react-native/use-design.d.ts +14 -0
- package/dist/react-native/use-design.js +72 -0
- package/dist/react-native/voice-launcher.js +6 -1
- package/dist/react-native/widget.js +171 -61
- package/dist/theme.d.ts +206 -0
- package/dist/theme.js +1999 -0
- package/package.json +1 -1
|
@@ -4,6 +4,9 @@ import { Image, Modal, Platform, Pressable, ScrollView, Text, View, useColorSche
|
|
|
4
4
|
import { Ctx, useGraineWidget } from "./index.js";
|
|
5
5
|
import { GraineLoader } from "./loader.js";
|
|
6
6
|
import { resolveNativeAppearance, resolveWidgetPalette } from "./appearance.js";
|
|
7
|
+
import { useDesign } from "./use-design.js";
|
|
8
|
+
import { alpha, letterSpacing, lineHeight, onAccentFor, shadowStyle, softFor } from "./design-math.js";
|
|
9
|
+
import { DEFAULT_DESIGN_PRESET, designPreset } from "../theme.js";
|
|
7
10
|
import { PLATFORM_GRAMMAR, manySummary, resolveLoader, resolvePlatform, resolveWidgetLook, widgetSurface } from "../protocol.js";
|
|
8
11
|
function tint(hex, alpha) {
|
|
9
12
|
const m = /^#?([0-9a-f]{6})$/i.exec(String(hex).trim());
|
|
@@ -12,55 +15,127 @@ function tint(hex, alpha) {
|
|
|
12
15
|
const n = parseInt(m[1], 16);
|
|
13
16
|
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`;
|
|
14
17
|
}
|
|
18
|
+
const CLASSIC_SCALE = designPreset(DEFAULT_DESIGN_PRESET).font.scale;
|
|
19
|
+
function classicSkin(theme, accent, radius) {
|
|
20
|
+
return {
|
|
21
|
+
classic: true,
|
|
22
|
+
size: (today) => today,
|
|
23
|
+
weight: (today) => today,
|
|
24
|
+
family: () => undefined,
|
|
25
|
+
line: (today) => today,
|
|
26
|
+
space: (today) => today,
|
|
27
|
+
pad: (h, v) => ({ paddingHorizontal: h, paddingVertical: v }),
|
|
28
|
+
gap: (compact) => (compact ? 6 : 10),
|
|
29
|
+
borderWidth: 1,
|
|
30
|
+
soft: (a) => tint(accent, a),
|
|
31
|
+
chip: tint(theme.border, 0.35),
|
|
32
|
+
choice: { fill: accent, text: theme.onAccent },
|
|
33
|
+
sheet: theme.surface,
|
|
34
|
+
page: theme.surface,
|
|
35
|
+
scrim: "rgba(0,0,0,0.45)",
|
|
36
|
+
rowRadius: Math.max(10, radius - 6),
|
|
37
|
+
imageRadius: Math.max(8, radius - 8),
|
|
38
|
+
slotRadius: 11,
|
|
39
|
+
sheetRadius: Math.max(20, radius),
|
|
40
|
+
depth: {
|
|
41
|
+
shadow: { shadowColor: "#000", shadowOpacity: 0.18, shadowRadius: 12, shadowOffset: { width: 0, height: 4 }, elevation: 6 },
|
|
42
|
+
hard: null, ink: "#000",
|
|
43
|
+
},
|
|
44
|
+
anim: (want) => want,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function designSkin(look, theme, accent, radius, ownRadius, surfaceOverride) {
|
|
48
|
+
const t = look.tokens;
|
|
49
|
+
const f = t.font;
|
|
50
|
+
const density = t.spacing.cardPadding / 14;
|
|
51
|
+
return {
|
|
52
|
+
classic: false,
|
|
53
|
+
size: (today, role, exact) => (exact ? f.scale[role] : Math.round(((today * f.scale[role]) / CLASSIC_SCALE[role]) * 10) / 10),
|
|
54
|
+
weight: (_today, which) => String(f.weights[which]),
|
|
55
|
+
family: (heading) => look.font(heading ? f.heading : f.body),
|
|
56
|
+
line: (_today, size, title) => lineHeight(size, title ? f.lineHeight.title : f.lineHeight.body),
|
|
57
|
+
space: (today) => Math.round(today * density),
|
|
58
|
+
pad: () => ({ paddingHorizontal: t.spacing.cardPadding, paddingVertical: t.spacing.cardPadding }),
|
|
59
|
+
gap: (compact) => (compact ? Math.round(t.spacing.gap * 0.6) : t.spacing.gap),
|
|
60
|
+
borderWidth: t.shape.borderWidth,
|
|
61
|
+
soft: () => softFor(accent, t),
|
|
62
|
+
chip: softFor(accent, t),
|
|
63
|
+
choice: { fill: softFor(accent, t), text: theme.text },
|
|
64
|
+
sheet: surfaceOverride ?? t.colors.surface,
|
|
65
|
+
page: surfaceOverride ?? t.colors.background,
|
|
66
|
+
scrim: alpha(t.colors.scrim, 0.4),
|
|
67
|
+
rowRadius: ownRadius ? Math.max(10, radius - 6) : t.shape.radius.md,
|
|
68
|
+
imageRadius: ownRadius ? Math.max(8, radius - 8) : t.shape.radius.sm,
|
|
69
|
+
slotRadius: t.shape.radius.md,
|
|
70
|
+
sheetRadius: ownRadius ? Math.max(20, radius) : t.shape.radius.xl,
|
|
71
|
+
depth: { shadow: shadowStyle(t.elevation.card), hard: t.elevation.hardOffset, ink: t.colors.text },
|
|
72
|
+
anim: (want) => (look.durationMs > 0 ? want : "none"),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function Raised({ k, style, children }) {
|
|
76
|
+
if (k.classic || !k.depth.hard) {
|
|
77
|
+
return _jsx(View, { style: k.classic ? style : [style, k.depth.shadow], children: children });
|
|
78
|
+
}
|
|
79
|
+
const { marginBottom, borderRadius, ...rest } = style;
|
|
80
|
+
const h = k.depth.hard;
|
|
81
|
+
return (_jsxs(View, { style: { marginBottom: (marginBottom ?? 0) + h.y, marginRight: h.x }, children: [_jsx(View, { pointerEvents: "none", style: { position: "absolute", top: h.y, left: h.x, right: -h.x, bottom: -h.y, borderRadius, backgroundColor: k.depth.ink } }), _jsx(View, { style: { ...rest, borderRadius, borderColor: k.depth.ink }, children: children })] }));
|
|
82
|
+
}
|
|
15
83
|
const HERO_HEIGHT = { small: 100, medium: 140, large: 180 };
|
|
16
84
|
const THUMB = { small: 56, medium: 72, large: 96 };
|
|
17
85
|
const LIST_THUMB = { small: 36, medium: 44, large: 56 };
|
|
18
|
-
function WidgetRow({ row, variant, look, grammar, theme, accent,
|
|
86
|
+
function WidgetRow({ row, variant, look, grammar, theme, accent, k, picked, onPick, }) {
|
|
19
87
|
const many = picked !== null;
|
|
20
|
-
const r =
|
|
88
|
+
const r = k.rowRadius;
|
|
21
89
|
const tight = look.density === "compact";
|
|
22
|
-
const pad = (variant === "list" ? (tight ? 6 : 9) : (tight ? 7 : 10)) + grammar.padBoost;
|
|
23
|
-
const gap = tight
|
|
90
|
+
const pad = k.space(variant === "list" ? (tight ? 6 : 9) : (tight ? 7 : 10)) + grammar.padBoost;
|
|
91
|
+
const gap = k.gap(tight);
|
|
92
|
+
const edge = (on) => (on ? k.borderWidth + 1 : k.borderWidth);
|
|
24
93
|
const rowIsButton = many || look.button === "row";
|
|
25
94
|
const outline = look.button === "outline";
|
|
26
95
|
const label = row.unavailable ? "Unavailable" : row.selectLabel;
|
|
27
96
|
const a11y = `${row.selectLabel}: ${row.title}`;
|
|
28
97
|
const badge = row.subtitle ? (_jsx(View, { style: { flexDirection: "row" }, children: _jsx(Text, { style: {
|
|
29
|
-
color: accent, fontSize: 10.5, fontWeight: "800", letterSpacing: 0.4,
|
|
30
|
-
textTransform: "uppercase", backgroundColor:
|
|
98
|
+
color: accent, fontSize: k.size(10.5, "caption"), fontWeight: k.weight("800", "strong"), letterSpacing: 0.4,
|
|
99
|
+
textTransform: "uppercase", backgroundColor: k.soft(0.15), fontFamily: k.family(),
|
|
31
100
|
borderRadius: 999, paddingHorizontal: 8, paddingVertical: 3, overflow: "hidden",
|
|
32
101
|
}, children: row.subtitle }) })) : null;
|
|
33
|
-
const titleLine = (big) => (_jsxs(View, { style: { flexDirection: "row", alignItems: "baseline", gap: 8 }, children: [_jsx(Text, { numberOfLines: 2, style: {
|
|
34
|
-
color: theme.text,
|
|
35
|
-
|
|
102
|
+
const titleLine = (big) => (_jsxs(View, { style: { flexDirection: "row", alignItems: "baseline", gap: 8 }, children: [_jsx(Text, { numberOfLines: 2, style: {
|
|
103
|
+
color: theme.text, fontWeight: grammar.titleWeight, flex: 1,
|
|
104
|
+
fontSize: big ? k.size(16.5, "title", true) : k.size(variant === "list" ? 13.5 : 14.5, "body"),
|
|
105
|
+
fontFamily: k.family(big),
|
|
106
|
+
}, children: row.title }), row.price ? (_jsx(Text, { style: {
|
|
107
|
+
color: theme.text, fontSize: big ? k.size(17, "title") : k.size(variant === "list" ? 13.5 : 14.5, "body"), fontWeight: k.weight("800", "strong"),
|
|
108
|
+
fontVariant: ["tabular-nums"], fontFamily: k.family(),
|
|
36
109
|
}, children: row.price })) : null] }));
|
|
37
110
|
const facts = look.facts && row.facts.length ? (_jsx(View, { style: { flexDirection: "row", flexWrap: "wrap", gap: 6, marginTop: 2 }, children: (variant === "list" ? row.facts.slice(0, 2) : row.facts).map((f, i) => (_jsx(Text, { style: {
|
|
38
|
-
color: theme.muted, fontSize: variant === "list" ? 10.5 : 11.5, fontWeight: "600",
|
|
39
|
-
|
|
40
|
-
|
|
111
|
+
color: theme.muted, fontSize: k.size(variant === "list" ? 10.5 : 11.5, "caption"), fontWeight: k.weight("600", "strong"),
|
|
112
|
+
fontFamily: k.family(),
|
|
113
|
+
backgroundColor: grammar.chipOutlined ? "transparent" : k.chip,
|
|
114
|
+
borderWidth: grammar.chipOutlined ? k.borderWidth : 0, borderColor: theme.border,
|
|
41
115
|
borderRadius: grammar.chipRadius, paddingHorizontal: variant === "list" ? 6 : 8, paddingVertical: variant === "list" ? 2 : 4, overflow: "hidden",
|
|
42
116
|
}, children: f }, `${f}-${i}`))) })) : null;
|
|
43
117
|
const tick = (_jsx(View, { style: {
|
|
44
118
|
width: 22, height: 22, borderRadius: 999, alignItems: "center", justifyContent: "center",
|
|
45
119
|
backgroundColor: picked ? accent : "transparent",
|
|
46
|
-
borderWidth: picked ? 0 :
|
|
120
|
+
borderWidth: picked ? 0 : k.borderWidth + 0.5, borderColor: theme.border,
|
|
47
121
|
}, children: picked ? _jsx(Text, { style: { color: theme.onAccent, fontSize: 12, fontWeight: "800" }, children: "\u2713" }) : null }));
|
|
48
122
|
const button = (wide) => (_jsx(Pressable, { disabled: row.unavailable, onPress: onPick, accessibilityRole: "button", accessibilityLabel: a11y, hitSlop: wide ? undefined : 8, style: {
|
|
49
123
|
minHeight: 44, justifyContent: "center", alignItems: "center",
|
|
50
124
|
borderRadius: wide ? grammar.buttonRadius : (grammar.pillButtons ? 999 : grammar.buttonRadius), paddingHorizontal: wide ? 12 : 14,
|
|
51
125
|
marginTop: wide ? 8 : 0,
|
|
52
126
|
backgroundColor: row.unavailable || outline ? "transparent" : accent,
|
|
53
|
-
borderWidth: row.unavailable || outline ?
|
|
127
|
+
borderWidth: row.unavailable || outline ? k.borderWidth + 0.5 : 0,
|
|
54
128
|
borderColor: row.unavailable ? theme.border : accent,
|
|
55
129
|
}, children: _jsx(Text, { style: {
|
|
56
130
|
color: row.unavailable ? theme.muted : (outline ? accent : theme.onAccent),
|
|
57
|
-
fontSize: wide ? 14 : 12.5, fontWeight: "700",
|
|
131
|
+
fontSize: wide ? k.size(14, "body") : k.size(12.5, "caption"), fontWeight: k.weight("700", "strong"),
|
|
132
|
+
fontFamily: k.family(),
|
|
58
133
|
}, children: label }) }));
|
|
59
|
-
const image = (style) => row.image ? (_jsx(Image, { source: { uri: row.image }, accessibilityIgnoresInvertColors: true, resizeMode: "cover", style: { borderRadius:
|
|
134
|
+
const image = (style) => row.image ? (_jsx(Image, { source: { uri: row.image }, accessibilityIgnoresInvertColors: true, resizeMode: "cover", style: { borderRadius: k.imageRadius, backgroundColor: theme.border, ...style } })) : null;
|
|
60
135
|
const wrap = (children, style) => rowIsButton ? (_jsx(Pressable, { disabled: row.unavailable, onPress: onPick, accessibilityRole: "button", accessibilityLabel: a11y, style: style, children: children })) : (_jsx(View, { style: style, children: children }));
|
|
61
136
|
if (variant === "hero") {
|
|
62
137
|
return wrap(_jsxs(_Fragment, { children: [image({ width: "100%", height: HERO_HEIGHT[look.imageSize] }), _jsxs(View, { style: { gap: 3 }, children: [badge, titleLine(true), facts, many ? null : rowIsButton ? null : button(true)] }), many ? _jsx(View, { style: { position: "absolute", top: pad + 6, right: pad + 6 }, children: tick }) : null] }), {
|
|
63
|
-
borderRadius: r, borderWidth: picked
|
|
138
|
+
borderRadius: r, borderWidth: edge(picked), borderColor: picked ? accent : theme.border, padding: pad,
|
|
64
139
|
opacity: row.unavailable ? 0.55 : 1, gap, flexDirection: "column",
|
|
65
140
|
});
|
|
66
141
|
}
|
|
@@ -69,13 +144,13 @@ function WidgetRow({ row, variant, look, grammar, theme, accent, radius, picked,
|
|
|
69
144
|
return wrap(_jsxs(_Fragment, { children: [image({ width: t, height: t }), _jsxs(View, { style: { flex: 1, minWidth: 0, gap: 2 }, children: [titleLine(false), facts] }), many ? tick : rowIsButton ? (_jsx(Text, { style: { color: theme.muted, fontSize: 22, fontWeight: "600", paddingLeft: 4 }, children: "›" })) : button(false)] }), {
|
|
70
145
|
flexDirection: "row", alignItems: "center", gap,
|
|
71
146
|
paddingVertical: pad, paddingHorizontal: 2, minHeight: 44,
|
|
72
|
-
borderBottomWidth:
|
|
147
|
+
borderBottomWidth: k.borderWidth, borderBottomColor: theme.border, marginLeft: grammar.separatorInset,
|
|
73
148
|
opacity: row.unavailable ? 0.55 : 1,
|
|
74
149
|
});
|
|
75
150
|
}
|
|
76
151
|
if (variant === "compact") {
|
|
77
152
|
return wrap(_jsxs(_Fragment, { children: [_jsxs(View, { style: { flex: 1, minWidth: 0, gap: 2 }, children: [badge, titleLine(false), facts] }), many ? tick : rowIsButton ? null : button(false)] }), {
|
|
78
|
-
borderRadius: r, borderWidth: picked
|
|
153
|
+
borderRadius: r, borderWidth: edge(picked), borderColor: picked ? accent : theme.border,
|
|
79
154
|
paddingVertical: pad, paddingHorizontal: pad + 2,
|
|
80
155
|
flexDirection: "row", alignItems: "center", gap,
|
|
81
156
|
opacity: row.unavailable ? 0.55 : 1, minHeight: 44,
|
|
@@ -83,11 +158,11 @@ function WidgetRow({ row, variant, look, grammar, theme, accent, radius, picked,
|
|
|
83
158
|
}
|
|
84
159
|
const t = THUMB[look.imageSize];
|
|
85
160
|
return wrap(_jsxs(_Fragment, { children: [image({ width: t, height: t }), _jsxs(View, { style: { flex: 1, minWidth: 0, gap: 3 }, children: [badge, titleLine(false), facts] }), many ? tick : rowIsButton ? null : button(false)] }), {
|
|
86
|
-
borderRadius: r, borderWidth: picked
|
|
161
|
+
borderRadius: r, borderWidth: edge(picked), borderColor: picked ? accent : theme.border, padding: pad,
|
|
87
162
|
opacity: row.unavailable ? 0.55 : 1, flexDirection: "row", gap, alignItems: "center",
|
|
88
163
|
});
|
|
89
164
|
}
|
|
90
|
-
function AnsweredLine({ answered, theme, accent, radius, awaiting, onClear }) {
|
|
165
|
+
function AnsweredLine({ answered, theme, accent, radius, k, awaiting, onClear }) {
|
|
91
166
|
const d = (answered.data || {});
|
|
92
167
|
const appearance = useContext(Ctx)?.appearance ?? null;
|
|
93
168
|
const loaderText = resolveLoader(appearance?.loader).text.thinking;
|
|
@@ -96,31 +171,66 @@ function AnsweredLine({ answered, theme, accent, radius, awaiting, onClear }) {
|
|
|
96
171
|
if (!title && !detail)
|
|
97
172
|
return null;
|
|
98
173
|
return (_jsxs(View, { style: {
|
|
99
|
-
backgroundColor: theme.surface, borderRadius: radius, borderWidth:
|
|
100
|
-
paddingHorizontal: 12, paddingVertical: 10, marginBottom: 8,
|
|
174
|
+
backgroundColor: theme.surface, borderRadius: radius, borderWidth: k.borderWidth, borderColor: theme.border,
|
|
175
|
+
paddingHorizontal: k.space(12), paddingVertical: k.space(10), marginBottom: 8,
|
|
101
176
|
flexDirection: "row", alignItems: "center", gap: 10,
|
|
102
177
|
}, children: [_jsx(View, { style: {
|
|
103
|
-
width: 26, height: 26, borderRadius: 999, backgroundColor:
|
|
178
|
+
width: 26, height: 26, borderRadius: 999, backgroundColor: k.soft(0.18),
|
|
104
179
|
alignItems: "center", justifyContent: "center",
|
|
105
|
-
}, children: awaiting ? _jsx(GraineLoader, { size: 14 }) : (_jsx(Text, { style: { color: accent, fontSize: 14, fontWeight: "800" }, children: "✓" })) }), _jsxs(View, { style: { flex: 1, minWidth: 0 }, children: [_jsx(Text, { numberOfLines: 1, style: { color: theme.text, fontSize: 13.5, fontWeight: "700" }, children: title || detail }), awaiting ? (_jsx(Text, { numberOfLines: 1, style: { color: theme.muted, fontSize: 11.5, fontWeight: "600" }, children: loaderText })) : title && detail ? (_jsx(Text, { numberOfLines: 1, style: { color: theme.muted, fontSize: 11.5, fontWeight: "600" }, children: detail })) : null] }), awaiting ? null : (_jsx(Pressable, { onPress: onClear, accessibilityRole: "button", accessibilityLabel: "Clear what I chose", hitSlop: 10, style: { minHeight: 44, justifyContent: "center", paddingHorizontal: 4 }, children: _jsx(Text, { style: { color: theme.muted, fontSize: 12, fontWeight: "700" }, children: "Clear" }) }))] }));
|
|
180
|
+
}, children: awaiting ? _jsx(GraineLoader, { size: 14 }) : (_jsx(Text, { style: { color: accent, fontSize: 14, fontWeight: "800" }, children: "✓" })) }), _jsxs(View, { style: { flex: 1, minWidth: 0 }, children: [_jsx(Text, { numberOfLines: 1, style: { color: theme.text, fontSize: k.size(13.5, "body"), fontWeight: k.weight("700", "strong"), fontFamily: k.family() }, children: title || detail }), awaiting ? (_jsx(Text, { numberOfLines: 1, style: { color: theme.muted, fontSize: k.size(11.5, "caption"), fontWeight: k.weight("600", "body"), fontFamily: k.family() }, children: loaderText })) : title && detail ? (_jsx(Text, { numberOfLines: 1, style: { color: theme.muted, fontSize: k.size(11.5, "caption"), fontWeight: k.weight("600", "body"), fontFamily: k.family() }, children: detail })) : null] }), awaiting ? null : (_jsx(Pressable, { onPress: onClear, accessibilityRole: "button", accessibilityLabel: "Clear what I chose", hitSlop: 10, style: { minHeight: 44, justifyContent: "center", paddingHorizontal: 4 }, children: _jsx(Text, { style: { color: theme.muted, fontSize: k.size(12, "caption"), fontWeight: k.weight("700", "strong"), fontFamily: k.family() }, children: "Clear" }) }))] }));
|
|
106
181
|
}
|
|
107
182
|
export function GraineWidget(props) {
|
|
108
183
|
const { widget, answered, loading, awaitingReply, submit, dismiss } = useGraineWidget();
|
|
109
184
|
const appearance = useContext(Ctx)?.appearance ?? null;
|
|
110
185
|
const scheme = useColorScheme();
|
|
186
|
+
const design = useDesign();
|
|
111
187
|
const [pagerWidth, setPagerWidth] = useState(0);
|
|
112
188
|
const [page, setPage] = useState(0);
|
|
113
189
|
const [ticked, setTicked] = useState([]);
|
|
114
190
|
const look = resolveWidgetLook(widget?.style, widget?.kind);
|
|
115
|
-
const
|
|
191
|
+
const phoneGrammar = PLATFORM_GRAMMAR[resolvePlatform(look.platform, Platform.OS, Platform.constants?.Manufacturer ?? Platform.constants?.Brand)];
|
|
116
192
|
const dash = resolveNativeAppearance(appearance, { accent: "#318CE7", heading: "" });
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
193
|
+
let accent;
|
|
194
|
+
let radius;
|
|
195
|
+
let theme;
|
|
196
|
+
let grammar;
|
|
197
|
+
let k;
|
|
198
|
+
if (design.classic) {
|
|
199
|
+
accent = props.accent ?? look.accent ?? dash.accent;
|
|
200
|
+
radius = props.radius ?? look.radius ?? (look.platform !== "auto" ? phoneGrammar.radius : dash.radius);
|
|
201
|
+
theme = {
|
|
202
|
+
...resolveWidgetPalette(appearance, { scheme, accent }),
|
|
203
|
+
...(props.theme ?? {}),
|
|
204
|
+
};
|
|
205
|
+
grammar = phoneGrammar;
|
|
206
|
+
k = classicSkin(theme, accent, radius);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const t = design.tokens;
|
|
210
|
+
const own = look.platform !== "auto";
|
|
211
|
+
accent = props.accent ?? look.accent ?? t.colors.accent;
|
|
212
|
+
radius = props.radius ?? look.radius ?? (own ? phoneGrammar.radius : t.shape.radius.lg);
|
|
213
|
+
theme = {
|
|
214
|
+
surface: t.colors.surfaceRaised, border: t.colors.border, text: t.colors.text, muted: t.colors.textMuted,
|
|
215
|
+
onAccent: onAccentFor(accent, t),
|
|
216
|
+
...(props.theme ?? {}),
|
|
217
|
+
};
|
|
218
|
+
grammar = own ? phoneGrammar : {
|
|
219
|
+
...phoneGrammar,
|
|
220
|
+
radius: t.shape.radius.lg, chipRadius: t.shape.radius.sm, buttonRadius: t.shape.radius.md,
|
|
221
|
+
pillButtons: false, titleWeight: String(t.font.weights.title), padBoost: 0, chipOutlined: false,
|
|
222
|
+
};
|
|
223
|
+
k = designSkin(design, theme, accent, radius, own || props.radius !== undefined || look.radius !== undefined, props.theme?.surface);
|
|
224
|
+
}
|
|
225
|
+
const titleType = (today) => k.classic
|
|
226
|
+
? { fontSize: today, fontWeight: "800" }
|
|
227
|
+
: {
|
|
228
|
+
fontSize: design.tokens.font.scale.title,
|
|
229
|
+
fontWeight: String(design.tokens.font.weights.title),
|
|
230
|
+
fontFamily: k.family(true),
|
|
231
|
+
letterSpacing: letterSpacing(design.tokens.font.scale.title, design.tokens.font.letterSpacing.title),
|
|
232
|
+
};
|
|
233
|
+
const chosen = answered ? (_jsx(AnsweredLine, { answered: answered, theme: theme, accent: accent, radius: radius, k: k, awaiting: awaitingReply, onClear: dismiss })) : null;
|
|
124
234
|
if (!widget && loading) {
|
|
125
235
|
const loader = resolveLoader(appearance?.loader);
|
|
126
236
|
const kindSurface = widgetSurface(loading.kind);
|
|
@@ -128,14 +238,14 @@ export function GraineWidget(props) {
|
|
|
128
238
|
const big = pendingSurface === "screen" ? 34 : pendingSurface === "sheet" ? 26 : undefined;
|
|
129
239
|
const mark = (_jsx(View, { style: { alignItems: "center", gap: 10 }, children: _jsx(GraineLoader, { moment: "component", size: big, stacked: true }) }));
|
|
130
240
|
const bone = (w, h) => (_jsx(View, { style: { width: w, height: h, borderRadius: 8, backgroundColor: tint(theme.border, 0.6) } }));
|
|
131
|
-
const bones = (rows) => (_jsx(View, { style: { gap: 10, opacity: 0.55 }, accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: Array.from({ length: rows }, (_, i) => (_jsxs(View, { style: { borderRadius:
|
|
241
|
+
const bones = (rows) => (_jsx(View, { style: { gap: 10, opacity: 0.55 }, accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: Array.from({ length: rows }, (_, i) => (_jsxs(View, { style: { borderRadius: k.rowRadius, borderWidth: k.borderWidth, borderColor: theme.border, padding: 10, flexDirection: "row", gap: 10, alignItems: "center" }, children: [bone(72, 72), _jsxs(View, { style: { flex: 1, gap: 8 }, children: [bone("70%", 12), bone("40%", 12), bone("55%", 10)] })] }, i))) }));
|
|
132
242
|
if (pendingSurface === "screen") {
|
|
133
|
-
return (_jsxs(_Fragment, { children: [chosen, _jsx(Modal, { visible: true, animationType: "fade", onRequestClose: dismiss, transparent: true, presentationStyle: "overFullScreen", statusBarTranslucent: true, children: _jsxs(View, { style: { flex: 1, backgroundColor:
|
|
243
|
+
return (_jsxs(_Fragment, { children: [chosen, _jsx(Modal, { visible: true, animationType: k.anim("fade"), onRequestClose: dismiss, transparent: true, presentationStyle: "overFullScreen", statusBarTranslucent: true, children: _jsxs(View, { style: { flex: 1, backgroundColor: k.page, paddingTop: 56, paddingHorizontal: 18 }, children: [_jsx(Pressable, { onPress: dismiss, accessibilityRole: "button", accessibilityLabel: "Close and keep talking", hitSlop: 10, style: { width: 44, height: 44, borderRadius: 999, alignItems: "center", justifyContent: "center", backgroundColor: tint(theme.border, 0.4) }, children: _jsx(Text, { style: { color: theme.text, fontSize: 20, fontWeight: "600" }, children: "\u00D7" }) }), _jsx(View, { style: { flex: 1, justifyContent: "center", paddingBottom: loader.skeleton ? 24 : 72 }, children: mark }), loader.skeleton ? bones(3) : null] }) })] }));
|
|
134
244
|
}
|
|
135
245
|
if (pendingSurface === "sheet") {
|
|
136
|
-
return (_jsxs(_Fragment, { children: [chosen, _jsx(Modal, { visible: true, animationType: "fade", transparent: true, presentationStyle: "overFullScreen", statusBarTranslucent: true, onRequestClose: dismiss, children: _jsxs(View, { style: { flex: 1, backgroundColor:
|
|
246
|
+
return (_jsxs(_Fragment, { children: [chosen, _jsx(Modal, { visible: true, animationType: k.anim("fade"), transparent: true, presentationStyle: "overFullScreen", statusBarTranslucent: true, onRequestClose: dismiss, children: _jsxs(View, { style: { flex: 1, backgroundColor: k.scrim, justifyContent: "flex-end" }, children: [_jsx(Pressable, { onPress: dismiss, accessibilityRole: "button", accessibilityLabel: "Close", style: { flex: 1 } }), _jsxs(View, { style: { backgroundColor: k.sheet, borderTopLeftRadius: k.sheetRadius, borderTopRightRadius: k.sheetRadius, paddingHorizontal: 18, paddingTop: 10, paddingBottom: 28, gap: 18 }, children: [_jsx(View, { style: { alignItems: "center" }, children: _jsx(View, { style: { width: grammar.grabber.width, height: grammar.grabber.height, borderRadius: 999, backgroundColor: theme.border } }) }), _jsx(View, { style: { paddingVertical: loader.skeleton ? 10 : 34 }, children: mark }), loader.skeleton ? bones(2) : null] })] }) })] }));
|
|
137
247
|
}
|
|
138
|
-
return (_jsxs(_Fragment, { children: [chosen, _jsx(
|
|
248
|
+
return (_jsxs(_Fragment, { children: [chosen, _jsx(Raised, { k: k, style: { backgroundColor: theme.surface, borderRadius: radius, borderWidth: k.borderWidth, borderColor: theme.border, ...k.pad(14, 14), marginBottom: 8, alignItems: "center" }, children: _jsx(GraineLoader, { moment: "component" }) })] }));
|
|
139
249
|
}
|
|
140
250
|
if (!widget)
|
|
141
251
|
return chosen;
|
|
@@ -214,24 +324,24 @@ export function GraineWidget(props) {
|
|
|
214
324
|
}, manySummary(titles, normalised.selectLabel.toLowerCase()));
|
|
215
325
|
setTicked([]);
|
|
216
326
|
};
|
|
217
|
-
const body = (_jsx(_Fragment, { children: isCalendar ? (_jsxs(View, { style: { gap: 12 }, children: [data.note ? (_jsx(Text, { style: { color: theme.muted, fontSize: 12.5, lineHeight: 18 }, children: String(data.note) })) : null, data.days.slice(0, 4).map((d, di) => {
|
|
327
|
+
const body = (_jsx(_Fragment, { children: isCalendar ? (_jsxs(View, { style: { gap: 12 }, children: [data.note ? (_jsx(Text, { style: { color: theme.muted, fontSize: k.size(12.5, "caption", true), lineHeight: k.line(18, k.size(12.5, "caption", true)), fontFamily: k.family() }, children: String(data.note) })) : null, data.days.slice(0, 4).map((d, di) => {
|
|
218
328
|
const dayLabel = String(d?.label ?? d?.id ?? `Day ${di + 1}`);
|
|
219
329
|
const slots = Array.isArray(d?.slots) ? d.slots.map(String) : [];
|
|
220
330
|
if (!slots.length)
|
|
221
331
|
return null;
|
|
222
|
-
return (_jsxs(View, { style: { gap: 7 }, children: [_jsx(Text, { style: { color: theme.text, fontSize: 13, fontWeight: "700" }, children: dayLabel }), _jsx(View, { style: { flexDirection: "row", flexWrap: "wrap", gap: 8 }, children: slots.slice(0, 8).map((slot, si) => (_jsx(Pressable, { onPress: () => submit({ day: dayLabel, slot }, `${dayLabel} at ${slot}, please.`), accessibilityRole: "button", accessibilityLabel: `${dayLabel} at ${slot}`, style: {
|
|
332
|
+
return (_jsxs(View, { style: { gap: 7 }, children: [_jsx(Text, { style: { color: theme.text, fontSize: k.size(13, "body"), fontWeight: k.weight("700", "strong"), fontFamily: k.family() }, children: dayLabel }), _jsx(View, { style: { flexDirection: "row", flexWrap: "wrap", gap: 8 }, children: slots.slice(0, 8).map((slot, si) => (_jsx(Pressable, { onPress: () => submit({ day: dayLabel, slot }, `${dayLabel} at ${slot}, please.`), accessibilityRole: "button", accessibilityLabel: `${dayLabel} at ${slot}`, style: {
|
|
223
333
|
minHeight: 44, justifyContent: "center",
|
|
224
|
-
borderWidth:
|
|
334
|
+
borderWidth: k.borderWidth, borderColor: theme.border, borderRadius: k.slotRadius,
|
|
225
335
|
paddingHorizontal: 14,
|
|
226
|
-
}, children: _jsx(Text, { style: { color: theme.text, fontSize: 13.5, fontWeight: "600" }, children: slot }) }, `${slot}-${si}`))) })] }, `${dayLabel}-${di}`));
|
|
336
|
+
}, children: _jsx(Text, { style: { color: theme.text, fontSize: k.size(13.5, "body"), fontWeight: k.weight("600", "strong"), fontFamily: k.family() }, children: slot }) }, `${slot}-${si}`))) })] }, `${dayLabel}-${di}`));
|
|
227
337
|
})] })) : isProducts ? ((() => {
|
|
228
338
|
const toggle = (id) => setTicked((cur) => cur.includes(id) ? cur.filter((x) => x !== id)
|
|
229
339
|
: cur.length >= look.maxSelect ? [...cur.slice(1), id]
|
|
230
340
|
: [...cur, id]);
|
|
231
|
-
const draw = (row, i, variant) => (_jsx(WidgetRow, { row: row, variant: variant, look: look, grammar: grammar, theme: theme, accent: accent,
|
|
341
|
+
const draw = (row, i, variant) => (_jsx(WidgetRow, { row: row, variant: variant, look: look, grammar: grammar, theme: theme, accent: accent, k: k, picked: many ? ticked.includes(row.id) : null, onPick: () => many
|
|
232
342
|
? toggle(row.id)
|
|
233
343
|
: pick({ ...normalised.items[i], id: row.id, title: row.title, price: row.price }) }, `${row.id}-${i}`));
|
|
234
|
-
const gap = look.density === "compact"
|
|
344
|
+
const gap = k.gap(look.density === "compact");
|
|
235
345
|
if (look.preset === "spotlight") {
|
|
236
346
|
const cardW = pagerWidth ? Math.round(pagerWidth * 0.86) : 300;
|
|
237
347
|
const step = cardW + gap;
|
|
@@ -265,7 +375,7 @@ export function GraineWidget(props) {
|
|
|
265
375
|
backgroundColor: chosenRows.length < 2 ? tint(theme.border, 0.5) : accent,
|
|
266
376
|
}, children: _jsx(Text, { style: {
|
|
267
377
|
color: chosenRows.length < 2 ? theme.muted : theme.onAccent,
|
|
268
|
-
fontSize: 14.5, fontWeight: "700",
|
|
378
|
+
fontSize: k.size(14.5, "body"), fontWeight: k.weight("700", "strong"), fontFamily: k.family(),
|
|
269
379
|
}, children: chosenRows.length < 2
|
|
270
380
|
? `Pick ${2 - chosenRows.length} more to ${normalised.selectLabel.toLowerCase()}`
|
|
271
381
|
: `${normalised.selectLabel} ${chosenRows.length}` }) })] }));
|
|
@@ -273,37 +383,37 @@ export function GraineWidget(props) {
|
|
|
273
383
|
const label = String(o?.label ?? o?.title ?? o ?? "");
|
|
274
384
|
const id = String(o?.id ?? label);
|
|
275
385
|
return (_jsx(Pressable, { onPress: () => submit(isQuickActions ? { id, label } : { chosen: id, label }, `I'll go with ${label}.`), accessibilityRole: "button", accessibilityLabel: label, style: {
|
|
276
|
-
backgroundColor:
|
|
386
|
+
backgroundColor: k.choice.fill, borderRadius: 999,
|
|
277
387
|
minHeight: 44, justifyContent: "center", paddingHorizontal: 16,
|
|
278
|
-
}, children: _jsx(Text, { style: { color:
|
|
279
|
-
}) })) : (_jsx(Text, { style: { color: theme.text, fontSize: 14, lineHeight: 20 }, children: widget.fallbackText || "The assistant has something to show you." })) }));
|
|
388
|
+
}, children: _jsx(Text, { style: { color: k.choice.text, fontSize: k.size(13.5, "body"), fontWeight: k.weight("700", "strong"), fontFamily: k.family() }, children: label }) }, `${id}-${i}`));
|
|
389
|
+
}) })) : (_jsx(Text, { style: { color: theme.text, fontSize: k.size(14, "body", true), lineHeight: k.line(20, k.size(14, "body", true)), fontFamily: k.family() }, children: widget.fallbackText || "The assistant has something to show you." })) }));
|
|
280
390
|
const title = data?.title ? String(data.title) : "";
|
|
281
391
|
const prompt = data?.prompt ? String(data.prompt) : "";
|
|
282
392
|
if (surface === "screen") {
|
|
283
|
-
return (_jsxs(_Fragment, { children: [chosen, _jsx(Modal, { visible: true, animationType: "slide", onRequestClose: dismiss, transparent: true, presentationStyle: "overFullScreen", statusBarTranslucent: true, children: _jsxs(View, { style: { flex: 1, backgroundColor:
|
|
393
|
+
return (_jsxs(_Fragment, { children: [chosen, _jsx(Modal, { visible: true, animationType: k.anim("slide"), onRequestClose: dismiss, transparent: true, presentationStyle: "overFullScreen", statusBarTranslucent: true, children: _jsxs(View, { style: { flex: 1, backgroundColor: k.page }, children: [_jsxs(View, { style: {
|
|
284
394
|
paddingTop: 52, paddingHorizontal: 18, paddingBottom: 12,
|
|
285
395
|
flexDirection: "row", alignItems: "center", gap: 12,
|
|
286
396
|
}, children: [_jsx(Pressable, { onPress: dismiss, accessibilityRole: "button", accessibilityLabel: "Close and keep talking", hitSlop: 10, style: {
|
|
287
397
|
width: 44, height: 44, borderRadius: 999, alignItems: "center", justifyContent: "center",
|
|
288
398
|
backgroundColor: tint(theme.border, 0.4),
|
|
289
|
-
}, children: _jsx(Text, { style: { color: theme.text, fontSize: 20, fontWeight: "600" }, children: "×" }) }), _jsxs(View, { style: { flex: 1, minWidth: 0 }, children: [title ? (_jsx(Text, { numberOfLines: 1, style: { color: theme.text,
|
|
399
|
+
}, children: _jsx(Text, { style: { color: theme.text, fontSize: 20, fontWeight: "600" }, children: "×" }) }), _jsxs(View, { style: { flex: 1, minWidth: 0 }, children: [title ? (_jsx(Text, { numberOfLines: 1, style: { color: theme.text, ...titleType(18) }, children: title })) : null, prompt ? (_jsx(Text, { numberOfLines: 1, style: { color: theme.muted, fontSize: k.size(12.5, "caption", true), fontWeight: k.weight("600", "body"), fontFamily: k.family(), marginTop: 1 }, children: prompt })) : null] })] }), _jsx(ScrollView, { style: { flex: 1 }, contentContainerStyle: { paddingHorizontal: 18, paddingBottom: 24 }, keyboardShouldPersistTaps: "handled", children: body }), _jsxs(View, { style: {
|
|
290
400
|
paddingHorizontal: 18, paddingTop: 12, paddingBottom: 28,
|
|
291
|
-
borderTopWidth:
|
|
292
|
-
}, children: [_jsx(Text, { style: { color: theme.muted, fontSize: 10.5, fontWeight: "800", letterSpacing: 0.4, textTransform: "uppercase" }, children: "Still listening" }), _jsx(Text, { numberOfLines: 1, style: { color: theme.text, fontSize: 13.5, marginTop: 2 }, children: prompt || "Tap one, or just say it." })] })] }) })] }));
|
|
401
|
+
borderTopWidth: k.borderWidth, borderTopColor: theme.border,
|
|
402
|
+
}, children: [_jsx(Text, { style: { color: theme.muted, fontSize: k.size(10.5, "caption"), fontWeight: k.weight("800", "strong"), fontFamily: k.family(), letterSpacing: 0.4, textTransform: "uppercase" }, children: "Still listening" }), _jsx(Text, { numberOfLines: 1, style: { color: theme.text, fontSize: k.size(13.5, "body", true), fontFamily: k.family(), marginTop: 2 }, children: prompt || "Tap one, or just say it." })] })] }) })] }));
|
|
293
403
|
}
|
|
294
404
|
if (surface === "sheet") {
|
|
295
|
-
return (_jsxs(_Fragment, { children: [chosen, _jsx(Modal, { visible: true, animationType: "slide", transparent: true, presentationStyle: "overFullScreen", statusBarTranslucent: true, onRequestClose: dismiss, children: _jsxs(View, { style: { flex: 1, backgroundColor:
|
|
296
|
-
backgroundColor:
|
|
297
|
-
borderTopLeftRadius:
|
|
405
|
+
return (_jsxs(_Fragment, { children: [chosen, _jsx(Modal, { visible: true, animationType: k.anim("slide"), transparent: true, presentationStyle: "overFullScreen", statusBarTranslucent: true, onRequestClose: dismiss, children: _jsxs(View, { style: { flex: 1, backgroundColor: k.scrim, justifyContent: "flex-end" }, children: [_jsx(Pressable, { onPress: dismiss, accessibilityRole: "button", accessibilityLabel: "Close", style: { flex: 1 } }), _jsxs(View, { style: {
|
|
406
|
+
backgroundColor: k.sheet,
|
|
407
|
+
borderTopLeftRadius: k.sheetRadius, borderTopRightRadius: k.sheetRadius,
|
|
298
408
|
paddingHorizontal: 18, paddingTop: 10, paddingBottom: 28, gap: 14,
|
|
299
|
-
}, children: [_jsx(View, { style: { alignItems: "center" }, children: _jsx(View, { style: { width: grammar.grabber.width, height: grammar.grabber.height, borderRadius: 999, backgroundColor: theme.border } }) }), title ? (_jsx(Text, { style: { color: theme.text,
|
|
409
|
+
}, children: [_jsx(View, { style: { alignItems: "center" }, children: _jsx(View, { style: { width: grammar.grabber.width, height: grammar.grabber.height, borderRadius: 999, backgroundColor: theme.border } }) }), title ? (_jsx(Text, { style: { color: theme.text, ...titleType(18) }, children: title })) : null, prompt ? (_jsx(Text, { style: { color: theme.muted, fontSize: k.size(13, "caption", true), lineHeight: k.line(18, k.size(13, "caption", true)), fontFamily: k.family(), marginTop: -8 }, children: prompt })) : null, _jsx(ScrollView, { style: { maxHeight: 420 }, contentContainerStyle: { paddingBottom: 4 }, keyboardShouldPersistTaps: "handled", children: body }), _jsx(Pressable, { onPress: dismiss, accessibilityRole: "button", accessibilityLabel: "None of these", style: {
|
|
300
410
|
alignSelf: "flex-start", minHeight: 44, justifyContent: "center",
|
|
301
411
|
paddingHorizontal: 16, borderRadius: 999, backgroundColor: tint(theme.border, 0.4),
|
|
302
|
-
}, children: _jsx(Text, { style: { color: theme.text, fontSize: 13.5, fontWeight: "700" }, children: "None of these" }) })] })] }) })] }));
|
|
412
|
+
}, children: _jsx(Text, { style: { color: theme.text, fontSize: k.size(13.5, "body"), fontWeight: k.weight("700", "strong"), fontFamily: k.family() }, children: "None of these" }) })] })] }) })] }));
|
|
303
413
|
}
|
|
304
|
-
return (_jsxs(_Fragment, { children: [chosen, _jsxs(
|
|
305
|
-
backgroundColor: theme.surface, borderRadius: radius, borderWidth:
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}, children: [title ? (_jsx(Text, { style: { color: theme.text,
|
|
414
|
+
return (_jsxs(_Fragment, { children: [chosen, _jsxs(Raised, { k: k, style: {
|
|
415
|
+
backgroundColor: theme.surface, borderRadius: radius, borderWidth: k.borderWidth, borderColor: theme.border,
|
|
416
|
+
...k.pad(14, 12), marginBottom: 8,
|
|
417
|
+
...(k.classic ? k.depth.shadow : null),
|
|
418
|
+
}, children: [title ? (_jsx(Text, { style: { color: theme.text, ...titleType(14.5), marginBottom: 2 }, children: title })) : null, prompt ? (_jsx(Text, { style: { color: theme.muted, fontSize: k.size(12.5, "caption", true), lineHeight: k.line(17, k.size(12.5, "caption", true)), fontFamily: k.family(), marginBottom: 8 }, children: prompt })) : _jsx(View, { style: { height: 6 } }), body, _jsx(Pressable, { onPress: dismiss, accessibilityRole: "button", accessibilityLabel: "Dismiss", hitSlop: 8, style: { alignSelf: "flex-end", marginTop: 10, minHeight: 44, justifyContent: "center" }, children: _jsx(Text, { style: { color: theme.muted, fontSize: k.size(12.5, "caption", true), fontWeight: k.weight("600", "body"), fontFamily: k.family() }, children: "Dismiss" }) })] })] }));
|
|
309
419
|
}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
export type DesignScheme = "auto" | "light" | "dark";
|
|
2
|
+
export type DesignDensity = "compact" | "comfortable" | "spacious";
|
|
3
|
+
export type DesignShadow = "preset" | "none" | "soft" | "medium" | "strong" | "hard";
|
|
4
|
+
export type DesignMotion = "preset" | "none" | "subtle" | "lively";
|
|
5
|
+
export type DesignNeutral = "preset" | "cool" | "neutral" | "warm";
|
|
6
|
+
export type DesignBubbles = "filled" | "outline" | "minimal";
|
|
7
|
+
export type LauncherShape = "circle" | "squircle" | "rounded-square" | "square" | "pill";
|
|
8
|
+
export type IconStyle = "outline" | "filled" | "duotone" | "sharp";
|
|
9
|
+
export type MotionEasing = "standard" | "emphasized" | "spring" | "linear" | "none";
|
|
10
|
+
export type DesignFamily = "graine" | "native" | "modern" | "enterprise" | "expressive";
|
|
11
|
+
export interface DesignConfig {
|
|
12
|
+
preset: string;
|
|
13
|
+
accent?: string;
|
|
14
|
+
scheme?: DesignScheme;
|
|
15
|
+
roundness?: number;
|
|
16
|
+
font?: string;
|
|
17
|
+
headingFont?: string;
|
|
18
|
+
density?: DesignDensity;
|
|
19
|
+
shadow?: DesignShadow;
|
|
20
|
+
motion?: DesignMotion;
|
|
21
|
+
neutral?: DesignNeutral;
|
|
22
|
+
glass?: boolean;
|
|
23
|
+
bubbles?: DesignBubbles;
|
|
24
|
+
launcherIcon?: string;
|
|
25
|
+
launcherShape?: LauncherShape;
|
|
26
|
+
iconStyle?: IconStyle;
|
|
27
|
+
}
|
|
28
|
+
export interface ThemeColors {
|
|
29
|
+
background: string;
|
|
30
|
+
surface: string;
|
|
31
|
+
surfaceRaised: string;
|
|
32
|
+
text: string;
|
|
33
|
+
textMuted: string;
|
|
34
|
+
border: string;
|
|
35
|
+
accent: string;
|
|
36
|
+
onAccent: string;
|
|
37
|
+
accentSoft: string;
|
|
38
|
+
success: string;
|
|
39
|
+
warning: string;
|
|
40
|
+
danger: string;
|
|
41
|
+
userBubble: string;
|
|
42
|
+
userBubbleText: string;
|
|
43
|
+
agentBubble: string;
|
|
44
|
+
agentBubbleText: string;
|
|
45
|
+
scrim: string;
|
|
46
|
+
}
|
|
47
|
+
export interface ThemeShadow {
|
|
48
|
+
y: number;
|
|
49
|
+
blur: number;
|
|
50
|
+
spread: number;
|
|
51
|
+
opacity: number;
|
|
52
|
+
color: string;
|
|
53
|
+
}
|
|
54
|
+
export interface ThemeTokens {
|
|
55
|
+
v: 1;
|
|
56
|
+
preset: string;
|
|
57
|
+
scheme: "light" | "dark";
|
|
58
|
+
colors: ThemeColors;
|
|
59
|
+
font: {
|
|
60
|
+
body: string;
|
|
61
|
+
heading: string;
|
|
62
|
+
scale: {
|
|
63
|
+
caption: number;
|
|
64
|
+
body: number;
|
|
65
|
+
title: number;
|
|
66
|
+
display: number;
|
|
67
|
+
};
|
|
68
|
+
weights: {
|
|
69
|
+
body: number;
|
|
70
|
+
strong: number;
|
|
71
|
+
title: number;
|
|
72
|
+
};
|
|
73
|
+
letterSpacing: {
|
|
74
|
+
body: number;
|
|
75
|
+
title: number;
|
|
76
|
+
};
|
|
77
|
+
lineHeight: {
|
|
78
|
+
body: number;
|
|
79
|
+
title: number;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
shape: {
|
|
83
|
+
radius: {
|
|
84
|
+
sm: number;
|
|
85
|
+
md: number;
|
|
86
|
+
lg: number;
|
|
87
|
+
xl: number;
|
|
88
|
+
};
|
|
89
|
+
borderWidth: number;
|
|
90
|
+
launcher: LauncherShape;
|
|
91
|
+
};
|
|
92
|
+
spacing: {
|
|
93
|
+
unit: number;
|
|
94
|
+
cardPadding: number;
|
|
95
|
+
gap: number;
|
|
96
|
+
};
|
|
97
|
+
elevation: {
|
|
98
|
+
card: ThemeShadow | null;
|
|
99
|
+
floating: ThemeShadow | null;
|
|
100
|
+
hardOffset: {
|
|
101
|
+
x: number;
|
|
102
|
+
y: number;
|
|
103
|
+
} | null;
|
|
104
|
+
};
|
|
105
|
+
motion: {
|
|
106
|
+
durationMs: number;
|
|
107
|
+
easing: MotionEasing;
|
|
108
|
+
bounce: number;
|
|
109
|
+
};
|
|
110
|
+
icons: {
|
|
111
|
+
style: IconStyle;
|
|
112
|
+
stroke: number;
|
|
113
|
+
launcher: {
|
|
114
|
+
id: string;
|
|
115
|
+
path: string;
|
|
116
|
+
stroke: boolean;
|
|
117
|
+
} | null;
|
|
118
|
+
};
|
|
119
|
+
glass: {
|
|
120
|
+
blur: number;
|
|
121
|
+
surfaceOpacity: number;
|
|
122
|
+
} | null;
|
|
123
|
+
bubbles: DesignBubbles;
|
|
124
|
+
}
|
|
125
|
+
export interface ResolvedDesign {
|
|
126
|
+
config: DesignConfig;
|
|
127
|
+
light: ThemeTokens;
|
|
128
|
+
dark: ThemeTokens;
|
|
129
|
+
}
|
|
130
|
+
export interface DesignPreset {
|
|
131
|
+
id: string;
|
|
132
|
+
name: string;
|
|
133
|
+
tagline: string;
|
|
134
|
+
family: DesignFamily;
|
|
135
|
+
inspiredBy: string[];
|
|
136
|
+
font: ThemeTokens["font"];
|
|
137
|
+
shape: ThemeTokens["shape"];
|
|
138
|
+
spacing: ThemeTokens["spacing"];
|
|
139
|
+
elevation: ThemeTokens["elevation"];
|
|
140
|
+
motion: ThemeTokens["motion"];
|
|
141
|
+
icons: {
|
|
142
|
+
style: IconStyle;
|
|
143
|
+
stroke: number;
|
|
144
|
+
launcher: string;
|
|
145
|
+
};
|
|
146
|
+
glass: ThemeTokens["glass"];
|
|
147
|
+
bubbles: DesignBubbles;
|
|
148
|
+
colors: {
|
|
149
|
+
light: Omit<ThemeColors, "accentSoft">;
|
|
150
|
+
dark: Omit<ThemeColors, "accentSoft">;
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export declare const DESIGN_SCHEMES: ReadonlyArray<DesignScheme>;
|
|
154
|
+
export declare const DESIGN_DENSITIES: ReadonlyArray<DesignDensity>;
|
|
155
|
+
export declare const DESIGN_SHADOWS: ReadonlyArray<DesignShadow>;
|
|
156
|
+
export declare const DESIGN_MOTIONS: ReadonlyArray<DesignMotion>;
|
|
157
|
+
export declare const DESIGN_NEUTRALS: ReadonlyArray<DesignNeutral>;
|
|
158
|
+
export declare const DESIGN_BUBBLES: ReadonlyArray<DesignBubbles>;
|
|
159
|
+
export declare const LAUNCHER_SHAPES: ReadonlyArray<LauncherShape>;
|
|
160
|
+
export declare const ICON_STYLES: ReadonlyArray<IconStyle>;
|
|
161
|
+
export declare function normaliseHex(value: unknown): string | null;
|
|
162
|
+
export declare function mixHex(a: string, b: string, t: number): string;
|
|
163
|
+
export declare function luminanceOf(hex: string): number;
|
|
164
|
+
export declare function contrastRatio(a: string, b: string): number;
|
|
165
|
+
export declare function readableOn(bg: string, candidates?: ReadonlyArray<string>): string;
|
|
166
|
+
export declare function ensureContrast(fg: string, bg: string, min?: number): string;
|
|
167
|
+
export declare const LAUNCHER_ICONS: ReadonlyArray<{
|
|
168
|
+
id: string;
|
|
169
|
+
label: string;
|
|
170
|
+
path: string;
|
|
171
|
+
stroke: boolean;
|
|
172
|
+
}>;
|
|
173
|
+
export declare const DESIGN_FONTS: ReadonlyArray<{
|
|
174
|
+
family: string;
|
|
175
|
+
category: "sans" | "serif" | "mono" | "display";
|
|
176
|
+
scripts: string[];
|
|
177
|
+
}>;
|
|
178
|
+
export declare const DESIGN_PRESETS: ReadonlyArray<DesignPreset>;
|
|
179
|
+
export declare const DEFAULT_DESIGN_PRESET = "graine-classic";
|
|
180
|
+
export declare function designPreset(id: unknown): DesignPreset;
|
|
181
|
+
export declare function normaliseDesignConfig(raw: unknown): DesignConfig;
|
|
182
|
+
export declare function designFromAppearance(appearance: unknown): DesignConfig;
|
|
183
|
+
export declare function legacyAppearanceFor(config: DesignConfig): {
|
|
184
|
+
accent: string;
|
|
185
|
+
radius: number;
|
|
186
|
+
theme: DesignScheme;
|
|
187
|
+
};
|
|
188
|
+
export declare const ON_ACCENT_MIN = 3;
|
|
189
|
+
export declare function resolveTokens(configInput: DesignConfig, scheme: "light" | "dark"): ThemeTokens;
|
|
190
|
+
export declare function resolveDesign(configInput: unknown): ResolvedDesign;
|
|
191
|
+
export declare function pickTokens(design: ResolvedDesign, deviceScheme: "light" | "dark" | null | undefined): ThemeTokens;
|
|
192
|
+
export declare const GLYPH_ASSET_VERSION = 2;
|
|
193
|
+
export declare function glyphParams(raw: {
|
|
194
|
+
id?: unknown;
|
|
195
|
+
color?: unknown;
|
|
196
|
+
size?: unknown;
|
|
197
|
+
stroke?: unknown;
|
|
198
|
+
}): {
|
|
199
|
+
id: string;
|
|
200
|
+
color: string;
|
|
201
|
+
size: number;
|
|
202
|
+
stroke: number;
|
|
203
|
+
};
|
|
204
|
+
export declare function glyphUrl(origin: string, id: string, color: string, sizePx: number, stroke?: number): string;
|
|
205
|
+
export declare function encodeDesignParam(config: DesignConfig): string;
|
|
206
|
+
export declare function decodeDesignParam(param: string | null | undefined): DesignConfig | null;
|