Mountain/ApplicationState/DTO/WebviewStateDTO.rs
1//! # WebviewStateDTO
2//!
3//! # RESPONSIBILITY
4//! - Data transfer object for Webview panel state
5//! - Serializable format for gRPC/IPC transmission
6//! - Used by Mountain to track Webview lifecycle
7//!
8//! # FIELDS
9//! - Handle: Unique Webview UUID
10//! - ViewType: Extension-defined view type
11//! - Title: Current panel title
12//! - ContentOptions: Web content and security settings
13//! - PanelOptions: Panel behavior options
14//! - SideCarIdentifier: Host sidecar process ID
15//! - ExtensionIdentifier: Owner extension ID
16//! - IsActive: Focus state flag
17//! - IsVisible: Visibility state flag
18use CommonLibrary::Webview::DTO::WebviewContentOptionsDTO::WebviewContentOptionsDTO;
19use serde::{Deserialize, Serialize};
20// For PanelOptions, etc.
21use serde_json::Value;
22
23/// Maximum handle length (UUID string)
24const MAX_HANDLE_LENGTH:usize = 128;
25
26/// Maximum view type length
27const MAX_VIEW_TYPE_LENGTH:usize = 128;
28
29/// Maximum sidecar identifier length
30const MAX_SIDECAR_IDENTIFIER_LENGTH:usize = 128;
31
32/// Maximum extension identifier length
33const MAX_EXTENSION_IDENTIFIER_LENGTH:usize = 128;
34
35/// Maximum title length
36const MAX_TITLE_LENGTH:usize = 256;
37
38/// A struct that holds the complete state for a single Webview panel instance.
39/// This is stored in `ApplicationState` to track all active Webviews managed by
40/// the host.
41#[derive(Serialize, Deserialize, Debug, Clone)]
42#[serde(rename_all = "camelCase")]
43pub struct WebviewStateDTO {
44 /// A unique UUID handle for this Webview instance.
45 pub Handle:String,
46
47 /// The view type of this Webview panel, as defined by the extension.
48 #[serde(skip_serializing_if = "String::is_empty")]
49 pub ViewType:String,
50
51 /// The current title of the Webview panel.
52 #[serde(skip_serializing_if = "String::is_empty")]
53 pub Title:String,
54
55 /// The content and security options for the Webview's content.
56 pub ContentOptions:WebviewContentOptionsDTO,
57
58 /// The options controlling the behavior of the Webview panel itself.
59 // DTO: WebviewPanelOptionsDTO
60 pub PanelOptions: Value,
61
62 /// The identifier of the sidecar process that owns this Webview.
63 #[serde(skip_serializing_if = "String::is_empty")]
64 pub SideCarIdentifier:String,
65
66 /// The identifier of the extension that owns this Webview.
67 #[serde(skip_serializing_if = "String::is_empty")]
68 pub ExtensionIdentifier:String,
69
70 /// A flag indicating if the Webview panel currently has focus.
71 pub IsActive:bool,
72
73 /// A flag indicating if the Webview panel is currently visible in the UI.
74 pub IsVisible:bool,
75}
76
77impl WebviewStateDTO {
78 /// Creates a new WebviewStateDTO with validation.
79 ///
80 /// # Arguments
81 /// * `Handle` - Unique Webview handle
82 /// * `ViewType` - Extension-defined view type
83 /// * `Title` - Panel title
84 /// * `ContentOptions` - Web content options
85 /// * `PanelOptions` - Panel behavior options
86 /// * `SideCarIdentifier` - Sidecar process ID
87 /// * `ExtensionIdentifier` - Extension ID
88 ///
89 /// # Returns
90 /// Result containing the DTO or validation error
91 pub fn New(
92 Handle:String,
93
94 ViewType:String,
95
96 Title:String,
97
98 ContentOptions:WebviewContentOptionsDTO,
99
100 PanelOptions:Value,
101
102 SideCarIdentifier:String,
103
104 ExtensionIdentifier:String,
105 ) -> Result<Self, String> {
106 // Validate handle length
107 if Handle.len() > MAX_HANDLE_LENGTH {
108 return Err(format!("Handle exceeds maximum length of {} bytes", MAX_HANDLE_LENGTH));
109 }
110
111 // Validate view type length
112 if ViewType.len() > MAX_VIEW_TYPE_LENGTH {
113 return Err(format!("ViewType exceeds maximum length of {} bytes", MAX_VIEW_TYPE_LENGTH));
114 }
115
116 // Validate title length
117 if Title.len() > MAX_TITLE_LENGTH {
118 return Err(format!("Title exceeds maximum length of {} bytes", MAX_TITLE_LENGTH));
119 }
120
121 // Validate sidecar identifier length
122 if SideCarIdentifier.len() > MAX_SIDECAR_IDENTIFIER_LENGTH {
123 return Err(format!(
124 "SideCar identifier exceeds maximum length of {} bytes",
125 MAX_SIDECAR_IDENTIFIER_LENGTH
126 ));
127 }
128
129 // Validate extension identifier length
130 if ExtensionIdentifier.len() > MAX_EXTENSION_IDENTIFIER_LENGTH {
131 return Err(format!(
132 "Extension identifier exceeds maximum length of {} bytes",
133 MAX_EXTENSION_IDENTIFIER_LENGTH
134 ));
135 }
136
137 Ok(Self {
138 Handle,
139 ViewType,
140 Title,
141 ContentOptions,
142 PanelOptions,
143 SideCarIdentifier,
144 ExtensionIdentifier,
145 IsActive:false,
146 IsVisible:false,
147 })
148 }
149
150 /// Updates the focus state of the Webview.
151 ///
152 /// # Arguments
153 /// * `IsActive` - New focus state
154 pub fn SetFocus(&mut self, IsActive:bool) { self.IsActive = IsActive; }
155
156 /// Updates the visibility state of the Webview.
157 ///
158 /// # Arguments
159 /// * `IsVisible` - New visibility state
160 pub fn SetVisibility(&mut self, IsVisible:bool) { self.IsVisible = IsVisible; }
161
162 /// Updates the Webview title with validation.
163 ///
164 /// # Arguments
165 /// * `Title` - New title
166 ///
167 /// # Returns
168 /// Result indicating success or error if title too long
169 pub fn UpdateTitle(&mut self, Title:String) -> Result<(), String> {
170 if Title.len() > MAX_TITLE_LENGTH {
171 return Err(format!("Title exceeds maximum length of {} bytes", MAX_TITLE_LENGTH));
172 }
173
174 self.Title = Title;
175
176 Ok(())
177 }
178
179 /// Checks if the Webview is currently displayed (visible and focused).
180 pub fn IsDisplayed(&self) -> bool { self.IsVisible || self.IsActive }
181}