androjack-mcp 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +34 -0
  3. package/.github/pull_request_template.md +16 -0
  4. package/CONTRIBUTING.md +27 -0
  5. package/LICENSE +21 -0
  6. package/README.md +592 -0
  7. package/SECURITY.md +26 -0
  8. package/assets/AndroJack banner.png +0 -0
  9. package/assets/killer_argument.png +0 -0
  10. package/build/constants.js +412 -0
  11. package/build/http-server.js +163 -0
  12. package/build/http.js +151 -0
  13. package/build/index.js +553 -0
  14. package/build/install.js +379 -0
  15. package/build/logger.js +57 -0
  16. package/build/tools/api-level.js +170 -0
  17. package/build/tools/api36-compliance.js +282 -0
  18. package/build/tools/architecture.js +75 -0
  19. package/build/tools/build-publish.js +362 -0
  20. package/build/tools/component.js +90 -0
  21. package/build/tools/debugger.js +82 -0
  22. package/build/tools/gradle.js +234 -0
  23. package/build/tools/kmp.js +348 -0
  24. package/build/tools/kotlin-patterns.js +500 -0
  25. package/build/tools/large-screen.js +366 -0
  26. package/build/tools/m3-expressive.js +447 -0
  27. package/build/tools/navigation3.js +331 -0
  28. package/build/tools/ondevice-ai.js +283 -0
  29. package/build/tools/permissions.js +404 -0
  30. package/build/tools/play-policy.js +221 -0
  31. package/build/tools/scalability.js +621 -0
  32. package/build/tools/search.js +89 -0
  33. package/build/tools/testing.js +439 -0
  34. package/build/tools/wear.js +337 -0
  35. package/build/tools/xr.js +274 -0
  36. package/config/antigravity_mcp.json +32 -0
  37. package/config/claude_desktop_config.json +17 -0
  38. package/config/cursor_mcp.json +21 -0
  39. package/config/jetbrains_mcp.json +28 -0
  40. package/config/kiro_mcp.json +40 -0
  41. package/config/vscode_mcp.json +24 -0
  42. package/config/windsurf_mcp.json +18 -0
  43. package/package.json +51 -0
  44. package/src/constants.ts +436 -0
  45. package/src/http-server.ts +186 -0
  46. package/src/http.ts +190 -0
  47. package/src/index.ts +702 -0
  48. package/src/install.ts +441 -0
  49. package/src/logger.ts +67 -0
  50. package/src/tools/api-level.ts +198 -0
  51. package/src/tools/api36-compliance.ts +289 -0
  52. package/src/tools/architecture.ts +94 -0
  53. package/src/tools/build-publish.ts +379 -0
  54. package/src/tools/component.ts +106 -0
  55. package/src/tools/debugger.ts +111 -0
  56. package/src/tools/gradle.ts +288 -0
  57. package/src/tools/kmp.ts +352 -0
  58. package/src/tools/kotlin-patterns.ts +534 -0
  59. package/src/tools/large-screen.ts +391 -0
  60. package/src/tools/m3-expressive.ts +473 -0
  61. package/src/tools/navigation3.ts +338 -0
  62. package/src/tools/ondevice-ai.ts +287 -0
  63. package/src/tools/permissions.ts +445 -0
  64. package/src/tools/play-policy.ts +229 -0
  65. package/src/tools/scalability.ts +646 -0
  66. package/src/tools/search.ts +112 -0
  67. package/src/tools/testing.ts +460 -0
  68. package/src/tools/wear.ts +343 -0
  69. package/src/tools/xr.ts +278 -0
  70. package/tsconfig.json +17 -0
@@ -0,0 +1,447 @@
1
+ /**
2
+ * Tool 8 – material3_expressive
3
+ *
4
+ * Complete reference for Material 3 Expressive — Google's design system
5
+ * announced May 2025, GA on Android 16 / QPR1 September 2025.
6
+ *
7
+ * Covers: new components, MaterialExpressiveTheme, MotionScheme,
8
+ * MaterialShapes, shape morphing, typography updates, migration from M3,
9
+ * Wear OS M3 Expressive, and anti-patterns.
10
+ *
11
+ * All knowledge sourced from:
12
+ * - developer.android.com/develop/ui/compose/designsystems/material3
13
+ * - developer.android.com/jetpack/androidx/releases/compose-material3
14
+ * - android-developers.googleblog.com (Androidify I/O 2025 post)
15
+ * - m3.material.io/blog/building-with-m3-expressive
16
+ */
17
+ import { secureFetch, extractPageText } from "../http.js";
18
+ // ── Knowledge base ───────────────────────────────────────────────────────────
19
+ const M3E_OVERVIEW = `
20
+ ## Material 3 Expressive (M3E) — Full Reference
21
+
22
+ **Announced:** Google I/O, May 2025
23
+ **GA on device:** Android 16 QPR1, September 2025 (Pixel 6+, Pixel Tablet)
24
+ **Compose dependency:** \`androidx.compose.material3:material3:1.4.x\`
25
+ **Opt-in annotation:** \`@OptIn(ExperimentalMaterial3ExpressiveApi::class)\`
26
+
27
+ M3 Expressive is NOT a new Material version ("Material 4").
28
+ It is an extension of Material You (M3 / Material Design 3) — same library,
29
+ new components and motion primitives layered on top.
30
+
31
+ ### What's new vs plain M3
32
+ | Area | M3 | M3 Expressive |
33
+ |------|-----|--------------|
34
+ | Theme entry point | \`MaterialTheme\` | \`MaterialExpressiveTheme\` (wraps MaterialTheme + expressive MotionScheme by default) |
35
+ | Motion | Static tweens | Physics-based \`MotionScheme\` (standard vs expressive) |
36
+ | Shapes | 5-step scale | 35-shape \`MaterialShapes\` library + shape morphing |
37
+ | Typography | Fixed weights | Variable fonts (weight + width axes) |
38
+ | New components | — | ButtonGroup, FloatingToolbar, DockedToolbar, FlexibleBottomAppBar, LoadingIndicator, SplitButtonLayout, FABMenu, ShortNavigationBar, WideNavigationRail |
39
+ | Bottom bar | \`BottomAppBar\` | \`DockedToolbar\` (BottomAppBar deprecated in favour of it) |
40
+ `;
41
+ const THEME_SETUP = `
42
+ ## Setting Up MaterialExpressiveTheme
43
+
44
+ ### Gradle dependency
45
+ \`\`\`kotlin
46
+ // build.gradle.kts (app)
47
+ implementation("androidx.compose.material3:material3:1.4.0-beta01") // check latest
48
+ \`\`\`
49
+
50
+ ### Theme wrapper
51
+ \`\`\`kotlin
52
+ // Source: android-developers.googleblog.com (Androidify, Google I/O 2025)
53
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
54
+ @Composable
55
+ fun AppTheme(content: @Composable () -> Unit) {
56
+ MaterialExpressiveTheme( // ← replaces MaterialTheme
57
+ colorScheme = dynamicColorScheme(LocalContext.current),
58
+ typography = Typography,
59
+ shapes = Shapes(),
60
+ motionScheme = MotionScheme.expressive(), // or .standard()
61
+ content = content
62
+ )
63
+ }
64
+ \`\`\`
65
+
66
+ ### MotionScheme tokens — use in custom animations
67
+ \`\`\`kotlin
68
+ // Spatial (position/size/shape changes)
69
+ val spatialSpec = MaterialTheme.motionScheme.defaultSpatialSpec<Float>()
70
+ // Effects (opacity, color, blur)
71
+ val effectsSpec = MaterialTheme.motionScheme.defaultEffectsSpec<Float>()
72
+
73
+ // Example: animate a shape change with the themed spec
74
+ val animatedSpec = MaterialTheme.motionScheme.defaultSpatialSpec<Float>()
75
+ val animatedCorner by animateFloatAsState(
76
+ targetValue = if (selected) 50f else 12f,
77
+ animationSpec = animatedSpec
78
+ )
79
+ \`\`\`
80
+
81
+ > ✅ GROUNDING GATE: Use \`MotionScheme.expressive()\` for playful consumer apps,
82
+ > \`MotionScheme.standard()\` for productivity/enterprise apps.
83
+ `;
84
+ const NEW_COMPONENTS = `
85
+ ## New & Updated M3 Expressive Components
86
+
87
+ ### 1. ButtonGroup
88
+ Groups related buttons with fluid shape-morphing on selection.
89
+ Requires \`@OptIn(ExperimentalMaterial3ExpressiveApi::class)\`.
90
+
91
+ \`\`\`kotlin
92
+ // Single-select ButtonGroup
93
+ // Source: proandroiddev.com M3 Expressive series + composables.com docs
94
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
95
+ @Composable
96
+ fun FilterButtonGroup() {
97
+ val options = listOf("All", "Unread", "Starred")
98
+ var selected by remember { mutableIntStateOf(0) }
99
+
100
+ ButtonGroup(overflowIndicator = {}) {
101
+ options.forEachIndexed { index, label ->
102
+ toggleableItem(
103
+ checked = selected == index,
104
+ onCheckedChange = { selected = index },
105
+ label = label
106
+ )
107
+ }
108
+ }
109
+ }
110
+
111
+ // Multi-select with custom connected shapes
112
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
113
+ @Composable
114
+ fun MultiSelectButtonGroup() {
115
+ val checked = remember { mutableStateListOf(false, false, false) }
116
+ Row(horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween)) {
117
+ listOf("Work", "Home", "Café").forEachIndexed { i, label ->
118
+ ToggleButton(
119
+ checked = checked[i],
120
+ onCheckedChange = { checked[i] = it },
121
+ shapes = when (i) {
122
+ 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes()
123
+ 2 -> ButtonGroupDefaults.connectedTrailingButtonShapes()
124
+ else -> ButtonGroupDefaults.connectedMiddleButtonShapes()
125
+ }
126
+ ) { Text(label) }
127
+ }
128
+ }
129
+ }
130
+ \`\`\`
131
+
132
+ ### 2. HorizontalFloatingToolbar / VerticalFloatingToolbar
133
+ Replaces the old FAB + BottomAppBar pattern. Collapses on scroll.
134
+
135
+ \`\`\`kotlin
136
+ // Source: proandroiddev.com M3 Expressive Part 2
137
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
138
+ @Composable
139
+ fun MainScreen() {
140
+ var expanded by rememberSaveable { mutableStateOf(true) }
141
+ Scaffold { padding ->
142
+ Box(Modifier.fillMaxSize()) {
143
+ LazyColumn(
144
+ modifier = Modifier.floatingToolbarVerticalNestedScroll(
145
+ expanded = expanded,
146
+ onExpand = { expanded = true },
147
+ onCollapse = { expanded = false }
148
+ ),
149
+ contentPadding = padding
150
+ ) { /* items */ }
151
+
152
+ HorizontalFloatingToolbar(
153
+ modifier = Modifier.align(Alignment.BottomCenter),
154
+ expanded = expanded,
155
+ floatingActionButton = {
156
+ FloatingToolbarDefaults.VibrantFloatingActionButton(onClick = { /*...*/ }) {
157
+ Icon(Icons.Filled.Add, contentDescription = "Add")
158
+ }
159
+ },
160
+ colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors()
161
+ ) {
162
+ IconButton(onClick = { }) { Icon(Icons.Filled.Edit, "Edit") }
163
+ IconButton(onClick = { }) { Icon(Icons.Filled.Share, "Share") }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ \`\`\`
169
+
170
+ ### 3. DockedToolbar (replaces BottomAppBar)
171
+ \`\`\`kotlin
172
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
173
+ @Composable
174
+ fun AppScaffold() {
175
+ Scaffold(
176
+ bottomBar = {
177
+ DockedToolbar { // ← use this, not BottomAppBar
178
+ IconButton(onClick = {}) { Icon(Icons.Filled.Home, "Home") }
179
+ IconButton(onClick = {}) { Icon(Icons.Filled.Search, "Search") }
180
+ }
181
+ }
182
+ ) { /* content */ }
183
+ }
184
+ \`\`\`
185
+
186
+ ### 4. LoadingIndicator (replaces CircularProgressIndicator for indeterminate)
187
+ \`\`\`kotlin
188
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
189
+ @Composable
190
+ fun LoadingScreen() {
191
+ Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
192
+ LoadingIndicator() // animated wavy M3E indicator
193
+ }
194
+ }
195
+ \`\`\`
196
+
197
+ ### 5. SplitButtonLayout
198
+ \`\`\`kotlin
199
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
200
+ @Composable
201
+ fun SaveSplitButton() {
202
+ SplitButtonLayout(
203
+ leadingButton = {
204
+ SplitButtonDefaults.LeadingButton(onClick = { /* primary action */ }) {
205
+ Text("Save")
206
+ }
207
+ },
208
+ trailingButton = {
209
+ SplitButtonDefaults.TrailingButton(onClick = { /* show dropdown */ }) {
210
+ Icon(Icons.Filled.ArrowDropDown, "More options")
211
+ }
212
+ }
213
+ )
214
+ }
215
+ \`\`\`
216
+
217
+ ### 6. FloatingActionButtonMenu (FABMenu)
218
+ \`\`\`kotlin
219
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
220
+ @Composable
221
+ fun SpeedDialFab() {
222
+ var expanded by rememberSaveable { mutableStateOf(false) }
223
+ FloatingActionButtonMenu(
224
+ expanded = expanded,
225
+ button = {
226
+ ToggleFloatingActionButton(
227
+ checked = expanded,
228
+ onCheckedChange = { expanded = it }
229
+ ) {
230
+ Icon(
231
+ if (expanded) Icons.Filled.Close else Icons.Filled.Add,
232
+ contentDescription = "Menu"
233
+ )
234
+ }
235
+ }
236
+ ) {
237
+ FloatingActionButtonMenuItem(onClick = {}, icon = { Icon(Icons.Filled.Edit, null) }, text = { Text("Edit") })
238
+ FloatingActionButtonMenuItem(onClick = {}, icon = { Icon(Icons.Filled.Share, null) }, text = { Text("Share") })
239
+ }
240
+ }
241
+ \`\`\`
242
+ `;
243
+ const MATERIALSHAPES = `
244
+ ## MaterialShapes — 35 Preset Morphable Shapes
245
+
246
+ M3 Expressive ships a \`MaterialShapes\` object with 35 named shapes
247
+ designed for smooth morphing between each other.
248
+
249
+ \`\`\`kotlin
250
+ // Source: android-developers.googleblog.com (Androidify I/O 2025)
251
+ import androidx.compose.material3.MaterialShapes
252
+
253
+ // Preset shapes (selection):
254
+ // MaterialShapes.Circle
255
+ // MaterialShapes.Square
256
+ // MaterialShapes.Pill
257
+ // MaterialShapes.Cookie4Sided
258
+ // MaterialShapes.Cookie6Sided
259
+ // MaterialShapes.Cookie9Sided ← used in Androidify
260
+ // MaterialShapes.Clover4Leaf
261
+ // MaterialShapes.Flower
262
+ // MaterialShapes.Burst8
263
+ // MaterialShapes.Slanted ...and more
264
+
265
+ // Convert to Compose Shape for clipping:
266
+ Box(
267
+ Modifier
268
+ .size(96.dp)
269
+ .clip(MaterialShapes.Cookie9Sided.toShape())
270
+ .background(MaterialTheme.colorScheme.primaryContainer)
271
+ )
272
+
273
+ // Morphing between shapes with animation:
274
+ val morph = remember { Morph(MaterialShapes.Circle, MaterialShapes.Square) }
275
+ val progress by animateFloatAsState(if (selected) 1f else 0f,
276
+ animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec())
277
+ Canvas(Modifier.size(64.dp)) {
278
+ drawPath(morph.toPath(progress, size), color = primaryColor)
279
+ }
280
+ \`\`\`
281
+ `;
282
+ const TYPOGRAPHY = `
283
+ ## M3 Expressive Typography — Variable Fonts
284
+
285
+ M3 Expressive uses variable fonts with weight and width axes.
286
+ The type scale is unchanged (displayLarge → labelSmall) but
287
+ weights and sizes are more expressive.
288
+
289
+ \`\`\`kotlin
290
+ // Custom typography with variable font weight
291
+ val Typography = Typography(
292
+ headlineLarge = TextStyle(
293
+ fontFamily = FontFamily(Font(R.font.roboto_flex)), // variable font
294
+ fontVariationSettings = FontVariationSettings(
295
+ FontVariation.weight(700f),
296
+ FontVariation.width(100f)
297
+ ),
298
+ fontSize = 32.sp,
299
+ lineHeight = 40.sp
300
+ )
301
+ // ... other styles
302
+ )
303
+
304
+ // In Compose — animated font weight (M3E pattern):
305
+ val fontWeight by animateFloatAsState(
306
+ if (isActive) 700f else 400f,
307
+ animationSpec = MaterialTheme.motionScheme.defaultEffectsSpec()
308
+ )
309
+ Text(
310
+ text = label,
311
+ fontVariationSettings = FontVariationSettings(FontVariation.weight(fontWeight))
312
+ )
313
+ \`\`\`
314
+ `;
315
+ const ANTI_PATTERNS = `
316
+ ## M3 Expressive Anti-Patterns
317
+
318
+ | ❌ Don't | ✅ Do Instead |
319
+ |---------|-------------|
320
+ | Use \`BottomAppBar\` for new screens | Use \`DockedToolbar\` |
321
+ | Mix \`MaterialTheme\` + expressive components | Wrap with \`MaterialExpressiveTheme\` |
322
+ | Use \`CircularProgressIndicator\` (indeterminate) where wavy fits | Use \`LoadingIndicator\` |
323
+ | Use \`SegmentedButton\` for filter pills | Use \`ButtonGroup\` with \`toggleableItem\` |
324
+ | Hardcode animation specs (\`spring()\`, \`tween()\`) | Pull from \`MaterialTheme.motionScheme.defaultSpatialSpec()\` |
325
+ | Use FAB + BottomAppBar combo | Use \`HorizontalFloatingToolbar\` with FAB slot |
326
+ | Skip \`@OptIn\` annotation | Always add \`@OptIn(ExperimentalMaterial3ExpressiveApi::class)\` |
327
+
328
+ ### BottomAppBar migration
329
+ \`\`\`kotlin
330
+ // ❌ Old — still works but deprecated in M3E context
331
+ Scaffold(bottomBar = {
332
+ BottomAppBar { /* actions */ }
333
+ })
334
+
335
+ // ✅ New
336
+ Scaffold(bottomBar = {
337
+ DockedToolbar { /* actions */ }
338
+ })
339
+ \`\`\`
340
+ `;
341
+ const WEAR_M3E = `
342
+ ## Wear OS — Material 3 Expressive
343
+
344
+ \`\`\`kotlin
345
+ // Wear Compose M3 dependency (source: developer.android.com/jetpack/androidx/releases/wear-compose-m3)
346
+ implementation("androidx.wear.compose:compose-material3:1.0.0-beta01")
347
+ \`\`\`
348
+
349
+ ### Key Wear M3E components
350
+ - **EdgeButton** — hugs the bottom edge of the round screen
351
+ - **ButtonGroup** — shape-morphs on touch, designed for round displays
352
+ - **TransformingLazyColumn** — built-in scroll animations that trace display curves
353
+ - **ConfirmationDialog** — success/failure/open-on-phone variants with timeout
354
+ - **Shape morphing** — IconButton, TextButton, IconToggleButton animate on press
355
+ - **Dynamic Color** — theme adapts to the user's chosen watch face
356
+
357
+ \`\`\`kotlin
358
+ // MotionScheme for Wear
359
+ val motionScheme = MotionScheme.expressive // companion object (Wear API)
360
+ // or
361
+ val motionScheme = MotionScheme.standard
362
+ // Set via MaterialTheme(motionScheme = ...) on Wear
363
+ \`\`\`
364
+ `;
365
+ const MIGRATION = `
366
+ ## Migrating from M3 to M3 Expressive
367
+
368
+ 1. **Update dependency** to \`material3:1.4.x\`
369
+ 2. **Replace \`MaterialTheme\`** with \`MaterialExpressiveTheme\`
370
+ 3. **Replace \`BottomAppBar\`** with \`DockedToolbar\`
371
+ 4. **Replace FAB + BottomAppBar combos** with \`HorizontalFloatingToolbar\`
372
+ 5. **Replace \`SegmentedButton\`** filter rows with \`ButtonGroup\`
373
+ 6. **Replace \`CircularProgressIndicator\` (indeterminate)** with \`LoadingIndicator\`
374
+ 7. **Replace hardcoded animation specs** with \`MaterialTheme.motionScheme.*\`
375
+ 8. **Add \`@OptIn\`** annotations to all files using new components
376
+
377
+ > 📚 Official migration guide: https://developer.android.com/develop/ui/compose/designsystems/material3
378
+ `;
379
+ const TOPICS = [
380
+ { keywords: ["overview", "what is", "intro", "m3e", "expressive"], content: M3E_OVERVIEW },
381
+ { keywords: ["theme", "setup", "motionscheme", "materialexpressivetheme", "getting started", "dependency", "gradle"], content: THEME_SETUP },
382
+ { keywords: ["component", "buttongroup", "floatingtoolbar", "dockedtoolbar", "loadingindicator", "splitbutton", "fabmenu", "floatingactionbutton", "new"], content: NEW_COMPONENTS },
383
+ { keywords: ["shape", "materialshapes", "morph", "morphing", "cookie", "squircle", "35"], content: MATERIALSHAPES },
384
+ { keywords: ["typography", "font", "variable", "weight", "width", "type"], content: TYPOGRAPHY },
385
+ { keywords: ["anti-pattern", "antipattern", "wrong", "avoid", "don't", "migrate", "migration", "from m3"], content: ANTI_PATTERNS + "\n\n" + MIGRATION },
386
+ { keywords: ["wear", "wearos", "watch", "pixel watch", "edgebutton", "transforminglazy"], content: WEAR_M3E },
387
+ ];
388
+ function findTopic(query) {
389
+ const lower = query.toLowerCase();
390
+ for (const topic of TOPICS) {
391
+ if (topic.keywords.some(k => lower.includes(k)))
392
+ return topic.content;
393
+ }
394
+ return null;
395
+ }
396
+ const INDEX = `
397
+ ## AndroJack — Material 3 Expressive Reference
398
+
399
+ **Query topics available:**
400
+
401
+ | Topic | Example query |
402
+ |-------|--------------|
403
+ | Overview & what's new | "m3 expressive overview" |
404
+ | Theme setup & MotionScheme | "MaterialExpressiveTheme setup" |
405
+ | New components | "ButtonGroup", "FloatingToolbar", "LoadingIndicator" |
406
+ | MaterialShapes & morphing | "shape morphing", "MaterialShapes" |
407
+ | Typography / variable fonts | "expressive typography" |
408
+ | Anti-patterns & migration | "migrate from M3", "anti-pattern" |
409
+ | Wear OS M3 Expressive | "wear expressive", "EdgeButton" |
410
+
411
+ **Official sources:**
412
+ - https://developer.android.com/develop/ui/compose/designsystems/material3
413
+ - https://m3.material.io/blog/building-with-m3-expressive
414
+ - https://developer.android.com/jetpack/androidx/releases/compose-material3
415
+ `;
416
+ // ── Main handler ─────────────────────────────────────────────────────────────
417
+ export async function material3Expressive(topic) {
418
+ const trimmed = topic.trim();
419
+ if (!trimmed || trimmed.toLowerCase() === "list" || trimmed.toLowerCase() === "help") {
420
+ return INDEX;
421
+ }
422
+ // Try local knowledge first (instant, no network)
423
+ const local = findTopic(trimmed);
424
+ if (local) {
425
+ return (local.trim() +
426
+ `\n\n---\n` +
427
+ `**Official docs:** https://developer.android.com/develop/ui/compose/designsystems/material3\n` +
428
+ `> 🎨 GROUNDING GATE: All M3 Expressive code must use \`MaterialExpressiveTheme\` and \`@OptIn(ExperimentalMaterial3ExpressiveApi::class)\`.`);
429
+ }
430
+ // Fallback: live fetch from official source
431
+ const url = `https://developer.android.com/s/results?q=${encodeURIComponent("material 3 expressive " + trimmed)}`;
432
+ try {
433
+ const html = await secureFetch(url);
434
+ const text = extractPageText(html, 2000);
435
+ return (`## M3 Expressive: "${trimmed}"\n\n` +
436
+ `No built-in entry found. Live results from developer.android.com:\n\n` +
437
+ text +
438
+ `\n\n**Search URL:** ${url}` +
439
+ `\n\n> 🎨 Verify against https://developer.android.com/develop/ui/compose/designsystems/material3`);
440
+ }
441
+ catch {
442
+ return (`## M3 Expressive: "${trimmed}"\n\n` +
443
+ `No built-in entry. Search manually:\n` +
444
+ `- https://developer.android.com/develop/ui/compose/designsystems/material3\n` +
445
+ `- https://m3.material.io/blog/building-with-m3-expressive`);
446
+ }
447
+ }