From 214c172c35264992736749a29f881984e4cd586e Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Thu, 12 Mar 2026 16:28:23 +0530 Subject: [PATCH 1/5] samples: update ACI samples for capability coupling rules - Fix swapped ImageSemantic/ImageOcr capability checks in AppIndexCapability - Add capability coupling comments to GetOrCreateIndex calls in all three ACI samples (SemanticSearch, KnowledgeRetrieval, AppIndexCapability) - Document that TextLexical suppression is only honored when TextSemantic is also Suppressed, and that ImageOcr/ImageSemantic are independent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../WCRAPIs/AppIndexCapability.xaml.cs | 19 +++++++++++++++++-- .../WCRAPIs/KnowledgeRetrieval.xaml.cs | 7 +++++++ .../Samples/WCRAPIs/SemanticSearch.xaml.cs | 7 +++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs index bd5c2d62..eb9d805d 100644 --- a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs @@ -40,6 +40,14 @@ protected override async Task LoadModelAsync(SampleNavigationParameters samplePa { await Task.Run(async () => { + // GetOrCreateIndex uses default options (all capabilities enabled). + // When using GetOrCreateIndexWithOptions, be aware of coupling rules: + // - Suppressing TextLexical is only honored when TextSemantic is also Suppressed; + // otherwise TextLexical suppression is silently treated as Default. + // - ImageOcr and ImageSemantic are independent; both must be Suppressed + // to fully disable image content. + // - Suppressing all text or all image capabilities makes that content kind + // unsupported (regions return UnsupportedContentKind). var result = AppContentIndexer.GetOrCreateIndex("indexCapabilityIndex"); if (!result.Succeeded) @@ -119,6 +127,10 @@ private async void LoadAppIndexCapabilities() // Each status will be one of: Unknown, Initialized, Initializing, Suppressed, Unsupported, DisabledByPolicy, InitializationError // If status is Initialized, that capability is ready for use + // + // Coupling rule: TextLexical suppression is only honored when + // TextSemantic is also Suppressed. If TextSemantic is Default or + // Required, TextLexical=Suppressed is silently treated as Default. if (capabilities.GetCapabilityState(IndexCapability.TextLexical).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) { indexLexicalCapabilityResultText.Text = "Available"; @@ -139,7 +151,10 @@ private async void LoadAppIndexCapabilities() unavailable.Add("TextSemantic"); } - if (capabilities.GetCapabilityState(IndexCapability.ImageSemantic).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) + // ImageOcr and ImageSemantic are independent capabilities; suppressing + // one does not affect the other. Both must be Suppressed to fully disable + // image content support. + if (capabilities.GetCapabilityState(IndexCapability.ImageOcr).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) { indexOCRCapabilityResultText.Text = "Available"; } @@ -149,7 +164,7 @@ private async void LoadAppIndexCapabilities() unavailable.Add("ImageOcr"); } - if (capabilities.GetCapabilityState(IndexCapability.ImageOcr).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) + if (capabilities.GetCapabilityState(IndexCapability.ImageSemantic).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) { indexSemanticImageCapabilityResultText.Text = "Available"; } diff --git a/AIDevGallery/Samples/WCRAPIs/KnowledgeRetrieval.xaml.cs b/AIDevGallery/Samples/WCRAPIs/KnowledgeRetrieval.xaml.cs index a3eef942..c00e851b 100644 --- a/AIDevGallery/Samples/WCRAPIs/KnowledgeRetrieval.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/KnowledgeRetrieval.xaml.cs @@ -105,6 +105,13 @@ await Task.Run(async () => } // Load AppContentIndexer + // GetOrCreateIndex uses default options (all capabilities enabled). + // To selectively suppress capabilities, use GetOrCreateIndexWithOptions. + // Coupling rules to keep in mind: + // - TextLexical suppression is only honored when TextSemantic is also + // Suppressed (semantic text requires the lexical pipeline). + // - ImageOcr and ImageSemantic are independent; both must be + // Suppressed to fully disable image content. var result = AppContentIndexer.GetOrCreateIndex("knowledgeRetrievalIndex"); if (!result.Succeeded) diff --git a/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs b/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs index ebc6be47..01344552 100644 --- a/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs @@ -82,6 +82,13 @@ protected override async Task LoadModelAsync(SampleNavigationParameters samplePa { await Task.Run(async () => { + // GetOrCreateIndex uses default options (all capabilities enabled). + // To selectively suppress capabilities, use GetOrCreateIndexWithOptions. + // Coupling rules to keep in mind: + // - TextLexical suppression is only honored when TextSemantic is also + // Suppressed (semantic text requires the lexical pipeline). + // - ImageOcr and ImageSemantic are independent; both must be + // Suppressed to fully disable image content. var result = AppContentIndexer.GetOrCreateIndex("semanticSearchIndex"); if (!result.Succeeded) From 694805bf1a80781154cb4b210a07cfb09eda0468 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Thu, 23 Apr 2026 15:43:42 +0530 Subject: [PATCH 2/5] enhance: Update AppIndexCapability and SemanticSearch to demonstrate suppressed capabilities and improve data handling --- .../Samples/WCRAPIs/AppIndexCapability.xaml | 147 ++++++++++-------- .../WCRAPIs/AppIndexCapability.xaml.cs | 63 ++++++++ .../Samples/WCRAPIs/SemanticSearch.xaml.cs | 27 +++- 3 files changed, 164 insertions(+), 73 deletions(-) diff --git a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml index cf6114b4..7332e293 100644 --- a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml +++ b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml @@ -8,71 +8,88 @@ xmlns:samples="using:AIDevGallery.Samples" mc:Ignorable="d"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + diff --git a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs index eb9d805d..cf81b799 100644 --- a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs @@ -77,6 +77,9 @@ await Task.Run(async () => sampleParams.NotifyCompletion(); }); + + // Demonstrate GetOrCreateIndexWithOptions with suppressed capabilities + await DemoSuppressedCapabilitiesAsync(); } protected override void OnNavigatedFrom(NavigationEventArgs e) @@ -187,6 +190,66 @@ private async void LoadAppIndexCapabilities() }); } + /// + /// Demonstrates GetOrCreateIndexWithOptions to show how capability suppression + /// affects index behavior. Uses a dedicated temp index name to avoid + /// IncompatibleWithExistingOptions errors. + /// + private async Task DemoSuppressedCapabilitiesAsync() + { + await Task.Run(() => + { + // First, delete any leftover temp index from a previous run + AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.FailIfInUse); + + // Suppress both image capabilities to fully disable image content support. + // Per the IDL coupling rules: + // - ImageOcr and ImageSemantic are independent; both must be Suppressed + // to fully disable image content. + // - If only one is Suppressed, image content is still partially supported. + var options = new GetOrCreateIndexOptions + { + ImageOcrRequirement = IndexCapabilityRequirement.Suppressed, + ImageSemanticRequirement = IndexCapabilityRequirement.Suppressed, + }; + + var result = AppContentIndexer.GetOrCreateIndex("capabilityDemoSuppressed", options); + + if (result.Succeeded) + { + using var tempIndexer = result.Indexer; + IndexCapabilities caps = tempIndexer.GetIndexCapabilities(); + + DispatcherQueue.TryEnqueue(() => + { + // Image capabilities should show as Suppressed + var ocrState = caps.GetCapabilityState(IndexCapability.ImageOcr).InitializationStatus; + var imgSemanticState = caps.GetCapabilityState(IndexCapability.ImageSemantic).InitializationStatus; + var textLexState = caps.GetCapabilityState(IndexCapability.TextLexical).InitializationStatus; + var textSemState = caps.GetCapabilityState(IndexCapability.TextSemantic).InitializationStatus; + + suppressedOcrText.Text = ocrState.ToString(); + suppressedImageSemanticText.Text = imgSemanticState.ToString(); + suppressedTextLexicalText.Text = textLexState.ToString(); + suppressedTextSemanticText.Text = textSemState.ToString(); + }); + } + else + { + DispatcherQueue.TryEnqueue(() => + { + suppressedOcrText.Text = $"Error: {result.Status}"; + suppressedImageSemanticText.Text = $"Error: {result.Status}"; + suppressedTextLexicalText.Text = $"Error: {result.Status}"; + suppressedTextSemanticText.Text = $"Error: {result.Status}"; + }); + } + + // Clean up the temporary index + AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.FailIfInUse); + }); + } + private void Listener_IndexCapabilitiesChanged(AppContentIndexer indexer, IndexCapabilities statusResult) { LoadAppIndexCapabilities(); diff --git a/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs b/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs index 01344552..78c4c184 100644 --- a/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs @@ -48,7 +48,7 @@ internal sealed partial class SemanticSearch : BaseSamplePage // This is some text data that we want to add to the index: private Dictionary simpleTextData = new Dictionary { - { "item1", "Preparing a hearty vegetable stew begins with chopping fresh carrots, onions, and celery. Sauté them in olive oil until fragrant, then add diced tomatoes, herbs, and vegetable broth. Simmer gently for an hour, allowing flavors to meld into a comforting dish perfect for cold evenings." }, + { "item1", "Preparing a hearty vegetable stew begins with chopping fresh carrots, onions, and celery. Saut� them in olive oil until fragrant, then add diced tomatoes, herbs, and vegetable broth. Simmer gently for an hour, allowing flavors to meld into a comforting dish perfect for cold evenings." }, { "item2", "Modern exhibition design combines narrative flow with spatial strategy. Lighting emphasizes focal objects while circulation paths avoid bottlenecks. Materials complement artifacts without visual competition. Interactive elements invite engagement but remain intuitive. Environmental controls protect sensitive works. Success balances scholarship, aesthetics, and visitor experience through thoughtful, cohesive design choices." }, { "item3", "Domestic cats communicate through posture, tail flicks, and vocalizations. Play mimics hunting behaviors like stalking and pouncing, supporting agility and mental stimulation. Scratching maintains claws and marks territory, so provide sturdy posts. Balanced diets, hydration, and routine veterinary care sustain health. Safe retreats and vertical spaces reduce stress and encourage exploration." }, { "item4", "Snowboarding across pristine slopes combines agility, balance, and speed. Riders carve smooth turns on powder, adjust stance for control, and master jumps in terrain parks. Essential gear includes boots, bindings, and helmets for safety. Embrace crisp alpine air while perfecting tricks and enjoying the thrill of winter adventure." }, @@ -281,15 +281,26 @@ private void SearchBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuery if (match.ContentKind == QueryMatchContentKind.AppManagedText) { AppManagedTextQueryMatch textResult = (AppManagedTextQueryMatch)match; - string matchingData = simpleTextData[match.ContentId]; - int offset = textResult.TextOffset; - int length = textResult.TextLength; - string matchingString = matchingData.Substring(offset, length); - textResults += matchingString + "\n\n"; + if (simpleTextData.TryGetValue(match.ContentId, out string? matchingData)) + { + int offset = textResult.TextOffset; + int length = textResult.TextLength; + + // Guard against stale offsets after content updates + if (offset >= 0 && offset < matchingData.Length && length > 0 && offset + length <= matchingData.Length) + { + string matchingString = matchingData.Substring(offset, length); + textResults += matchingString + "\n\n"; + } + else + { + textResults += matchingData + "\n\n"; + } + } } } - // Create text query + // Create image query AppIndexImageQuery imageQuery = _indexer.CreateImageQuery(searchText, imageQueryOptions); // Get image matches @@ -557,7 +568,7 @@ private async void LoadAppIndexCapabilities() // Disable text sample if both text capabilities are unavailable textDataItemsView.IsEnabled = textLexicalAvailable || textSemanticAvailable; - uploadTextButton.IsEnabled = imageSemanticAvailable || imageOcrAvailable; + uploadTextButton.IsEnabled = textLexicalAvailable || textSemanticAvailable; // Disable image sample if both image capabilities are unavailable ImageDataItemsView.IsEnabled = imageSemanticAvailable || imageOcrAvailable; From 7af8ac75e17dd0e5eb9e662df15921f064d584f8 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Mon, 27 Apr 2026 16:43:15 +0530 Subject: [PATCH 3/5] refactor: Simplify index capability suppression logic and improve error handling --- .../Samples/WCRAPIs/AppIndexCapability.xaml | 4 - .../WCRAPIs/AppIndexCapability.xaml.cs | 90 +++++++++++-------- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml index 7332e293..01c8e58c 100644 --- a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml +++ b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml @@ -10,10 +10,6 @@ - - - - diff --git a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs index cf81b799..db3c36d9 100644 --- a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs @@ -199,54 +199,68 @@ private async Task DemoSuppressedCapabilitiesAsync() { await Task.Run(() => { - // First, delete any leftover temp index from a previous run - AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.FailIfInUse); - - // Suppress both image capabilities to fully disable image content support. - // Per the IDL coupling rules: - // - ImageOcr and ImageSemantic are independent; both must be Suppressed - // to fully disable image content. - // - If only one is Suppressed, image content is still partially supported. - var options = new GetOrCreateIndexOptions + try { - ImageOcrRequirement = IndexCapabilityRequirement.Suppressed, - ImageSemanticRequirement = IndexCapabilityRequirement.Suppressed, - }; - - var result = AppContentIndexer.GetOrCreateIndex("capabilityDemoSuppressed", options); + // First, delete any leftover temp index from a previous run + AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.ForceDelete); + + // Suppress both image capabilities to fully disable image content support. + // Per the IDL coupling rules: + // - ImageOcr and ImageSemantic are independent; both must be Suppressed + // to fully disable image content. + // - If only one is Suppressed, image content is still partially supported. + var options = new GetOrCreateIndexOptions + { + ImageOcrRequirement = IndexCapabilityRequirement.Suppressed, + ImageSemanticRequirement = IndexCapabilityRequirement.Suppressed, + }; - if (result.Succeeded) - { - using var tempIndexer = result.Indexer; - IndexCapabilities caps = tempIndexer.GetIndexCapabilities(); + var result = AppContentIndexer.GetOrCreateIndex("capabilityDemoSuppressed", options); - DispatcherQueue.TryEnqueue(() => + if (result.Succeeded) { - // Image capabilities should show as Suppressed - var ocrState = caps.GetCapabilityState(IndexCapability.ImageOcr).InitializationStatus; - var imgSemanticState = caps.GetCapabilityState(IndexCapability.ImageSemantic).InitializationStatus; - var textLexState = caps.GetCapabilityState(IndexCapability.TextLexical).InitializationStatus; - var textSemState = caps.GetCapabilityState(IndexCapability.TextSemantic).InitializationStatus; - - suppressedOcrText.Text = ocrState.ToString(); - suppressedImageSemanticText.Text = imgSemanticState.ToString(); - suppressedTextLexicalText.Text = textLexState.ToString(); - suppressedTextSemanticText.Text = textSemState.ToString(); - }); + using var tempIndexer = result.Indexer; + IndexCapabilities caps = tempIndexer.GetIndexCapabilities(); + + // Read values before leaving the using block to avoid use-after-dispose + var ocrState = caps.GetCapabilityState(IndexCapability.ImageOcr).InitializationStatus.ToString(); + var imgSemanticState = caps.GetCapabilityState(IndexCapability.ImageSemantic).InitializationStatus.ToString(); + var textLexState = caps.GetCapabilityState(IndexCapability.TextLexical).InitializationStatus.ToString(); + var textSemState = caps.GetCapabilityState(IndexCapability.TextSemantic).InitializationStatus.ToString(); + + DispatcherQueue.TryEnqueue(() => + { + suppressedOcrText.Text = ocrState; + suppressedImageSemanticText.Text = imgSemanticState; + suppressedTextLexicalText.Text = textLexState; + suppressedTextSemanticText.Text = textSemState; + }); + } + else + { + DispatcherQueue.TryEnqueue(() => + { + suppressedOcrText.Text = $"Error: {result.Status}"; + suppressedImageSemanticText.Text = $"Error: {result.Status}"; + suppressedTextLexicalText.Text = $"Error: {result.Status}"; + suppressedTextSemanticText.Text = $"Error: {result.Status}"; + }); + } + + // Clean up the temporary index + AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.ForceDelete); } - else + catch (Exception ex) { DispatcherQueue.TryEnqueue(() => { - suppressedOcrText.Text = $"Error: {result.Status}"; - suppressedImageSemanticText.Text = $"Error: {result.Status}"; - suppressedTextLexicalText.Text = $"Error: {result.Status}"; - suppressedTextSemanticText.Text = $"Error: {result.Status}"; + suppressedOcrText.Text = "Error"; + suppressedImageSemanticText.Text = "Error"; + suppressedTextLexicalText.Text = "Error"; + suppressedTextSemanticText.Text = "Error"; + Debug.WriteLine($"DemoSuppressedCapabilitiesAsync failed: {ex.Message}"); }); } - - // Clean up the temporary index - AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.FailIfInUse); }); } From 02ef8a799b10e28e9d4a8056038c45e94146ecf3 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 28 Apr 2026 12:23:34 +0530 Subject: [PATCH 4/5] fix: Change index deletion behavior to defer if in use for capability suppression demo --- AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs index db3c36d9..14f09aeb 100644 --- a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs @@ -202,7 +202,7 @@ await Task.Run(() => try { // First, delete any leftover temp index from a previous run - AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.ForceDelete); + AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.DeferIfInUse); // Suppress both image capabilities to fully disable image content support. // Per the IDL coupling rules: @@ -248,7 +248,7 @@ await Task.Run(() => } // Clean up the temporary index - AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.ForceDelete); + AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.DeferIfInUse); } catch (Exception ex) { From 15a67f8b03bfb4f20c661af02a5ff8f3cc7c582c Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 28 Apr 2026 12:28:35 +0530 Subject: [PATCH 5/5] fix: Correct typo in vegetable stew description in SemanticSearch --- AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs b/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs index 78c4c184..4b2dcd5f 100644 --- a/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs @@ -48,7 +48,7 @@ internal sealed partial class SemanticSearch : BaseSamplePage // This is some text data that we want to add to the index: private Dictionary simpleTextData = new Dictionary { - { "item1", "Preparing a hearty vegetable stew begins with chopping fresh carrots, onions, and celery. Saut� them in olive oil until fragrant, then add diced tomatoes, herbs, and vegetable broth. Simmer gently for an hour, allowing flavors to meld into a comforting dish perfect for cold evenings." }, + { "item1", "Preparing a hearty vegetable stew begins with chopping fresh carrots, onions, and celery. Sauté them in olive oil until fragrant, then add diced tomatoes, herbs, and vegetable broth. Simmer gently for an hour, allowing flavors to meld into a comforting dish perfect for cold evenings." }, { "item2", "Modern exhibition design combines narrative flow with spatial strategy. Lighting emphasizes focal objects while circulation paths avoid bottlenecks. Materials complement artifacts without visual competition. Interactive elements invite engagement but remain intuitive. Environmental controls protect sensitive works. Success balances scholarship, aesthetics, and visitor experience through thoughtful, cohesive design choices." }, { "item3", "Domestic cats communicate through posture, tail flicks, and vocalizations. Play mimics hunting behaviors like stalking and pouncing, supporting agility and mental stimulation. Scratching maintains claws and marks territory, so provide sturdy posts. Balanced diets, hydration, and routine veterinary care sustain health. Safe retreats and vertical spaces reduce stress and encourage exploration." }, { "item4", "Snowboarding across pristine slopes combines agility, balance, and speed. Riders carve smooth turns on powder, adjust stance for control, and master jumps in terrain parks. Essential gear includes boots, bindings, and helmets for safety. Embrace crisp alpine air while perfecting tricks and enjoying the thrill of winter adventure." },