@pheem49/mint 1.8.0 → 1.8.2

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 (200) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/Cargo.lock +34 -3
  3. package/Cargo.toml +2 -2
  4. package/README.md +20 -6
  5. package/Release_Note.md +129 -0
  6. package/assets/Mint_CLI.png +0 -0
  7. package/bin/mint +0 -0
  8. package/crates/mint-cli/Cargo.toml +1 -0
  9. package/crates/mint-cli/src/agent.rs +242 -54
  10. package/crates/mint-cli/src/main.rs +397 -74
  11. package/crates/mint-core/src/agent_loop.rs +1 -0
  12. package/crates/mint-core/src/api_server.rs +64 -2
  13. package/crates/mint-core/src/browser.rs +456 -0
  14. package/crates/mint-core/src/channels.rs +1 -0
  15. package/crates/mint-core/src/chat.rs +3 -0
  16. package/crates/mint-core/src/config.rs +130 -1
  17. package/crates/mint-core/src/lib.rs +19 -0
  18. package/crates/mint-core/src/orchestration.rs +317 -12
  19. package/crates/mint-core/src/skills.rs +113 -4
  20. package/crates/mint-core/src/tts.rs +3 -1
  21. package/crates/mint-core/src/workflows.rs +1 -22
  22. package/flatpak/com.pheem49.mint.desktop +9 -0
  23. package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
  24. package/flatpak/com.pheem49.mint.yaml +52 -0
  25. package/package.json +1 -1
  26. package/shared-modules/.github/workflows/ci.yml +120 -0
  27. package/shared-modules/.github/workflows/update.yaml +28 -0
  28. package/shared-modules/CODEOWNERS +23 -0
  29. package/shared-modules/README.md +79 -0
  30. package/shared-modules/SDL/README.md +3 -0
  31. package/shared-modules/SDL/SDL-1.2.15.json +51 -0
  32. package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
  33. package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
  34. package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
  35. package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
  36. package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
  37. package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
  38. package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
  39. package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
  40. package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
  41. package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
  42. package/shared-modules/SDL/sdl12-compat.json +22 -0
  43. package/shared-modules/SDL2/SDL2_image.json +27 -0
  44. package/shared-modules/SDL2/SDL2_ttf.json +29 -0
  45. package/shared-modules/cld2/CMakeLists.txt +157 -0
  46. package/shared-modules/cld2/cld2.json +24 -0
  47. package/shared-modules/clutter/clutter.json +72 -0
  48. package/shared-modules/dbus-glib/dbus-glib.json +29 -0
  49. package/shared-modules/glew/glew.json +36 -0
  50. package/shared-modules/glu/glu-9.json +12 -0
  51. package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
  52. package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
  53. package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
  54. package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
  55. package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
  56. package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
  57. package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
  58. package/shared-modules/gtk2/gtk2.json +120 -0
  59. package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
  60. package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
  61. package/shared-modules/gtk2/readme.md +10 -0
  62. package/shared-modules/intltool/intltool-0.51.json +20 -0
  63. package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
  64. package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
  65. package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
  66. package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
  67. package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
  68. package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
  69. package/shared-modules/lame/lame-3.99.5.json +32 -0
  70. package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
  71. package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
  72. package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
  73. package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
  74. package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
  75. package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
  76. package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
  77. package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
  78. package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
  79. package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
  80. package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
  81. package/shared-modules/libcanberra/libcanberra.json +44 -0
  82. package/shared-modules/libmad/libmad.json +19 -0
  83. package/shared-modules/libsecret/libsecret.json +23 -0
  84. package/shared-modules/libsoup/libsoup-2.4.json +14 -0
  85. package/shared-modules/libusb/libusb.json +27 -0
  86. package/shared-modules/linux-audio/dssi.json +31 -0
  87. package/shared-modules/linux-audio/fftw3f-static.json +44 -0
  88. package/shared-modules/linux-audio/fftw3f.json +42 -0
  89. package/shared-modules/linux-audio/fluidsynth2-static.json +30 -0
  90. package/shared-modules/linux-audio/fluidsynth2.json +19 -0
  91. package/shared-modules/linux-audio/ladspa.json +26 -0
  92. package/shared-modules/linux-audio/lash.json +35 -0
  93. package/shared-modules/linux-audio/libinstpatch.json +16 -0
  94. package/shared-modules/linux-audio/liblo-static.json +22 -0
  95. package/shared-modules/linux-audio/liblo.json +17 -0
  96. package/shared-modules/linux-audio/lilv-static.json +94 -0
  97. package/shared-modules/linux-audio/lilv.json +82 -0
  98. package/shared-modules/linux-audio/lrdf.json +57 -0
  99. package/shared-modules/linux-audio/lv2.json +21 -0
  100. package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
  101. package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
  102. package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
  103. package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
  104. package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
  105. package/shared-modules/linux-audio/stk.json +28 -0
  106. package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
  107. package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
  108. package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
  109. package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
  110. package/shared-modules/lua5.3/lua.pc.in +8 -0
  111. package/shared-modules/lua5.3/luaconf.patch +47 -0
  112. package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
  113. package/shared-modules/lua5.4/lua-5.4.json +41 -0
  114. package/shared-modules/lua5.4/lua.pc.in +8 -0
  115. package/shared-modules/lua5.4/luaconf.patch +34 -0
  116. package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
  117. package/shared-modules/luajit/luajit.json +30 -0
  118. package/shared-modules/lzo/lzo.json +27 -0
  119. package/shared-modules/mac/mac.json +18 -0
  120. package/shared-modules/physfs/physfs.json +26 -0
  121. package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
  122. package/shared-modules/pygame/cython-fixes.patch +34 -0
  123. package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
  124. package/shared-modules/pygame/glib.patch +25 -0
  125. package/shared-modules/pygame/pygame1-python2.json +157 -0
  126. package/shared-modules/pygtk/pygtk-c99.patch +15 -0
  127. package/shared-modules/pygtk/pygtk.json +117 -0
  128. package/shared-modules/pygtk/pygtk.patch +39 -0
  129. package/shared-modules/python2.7/python-2.7.json +48 -0
  130. package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
  131. package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
  132. package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
  133. package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
  134. package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
  135. package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
  136. package/shared-modules/update-checker.sh +50 -0
  137. package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
  138. package/shared-modules/vorbisgain/0002-errno.patch +33 -0
  139. package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
  140. package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
  141. package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
  142. package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
  143. package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
  144. package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
  145. package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
  146. package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
  147. package/shared-modules/vorbisgain/README.md +23 -0
  148. package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
  149. package/src/renderer/shared/agentProgress.ts +145 -0
  150. package/src/renderer/shared/calculator.ts +52 -0
  151. package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
  152. package/src/renderer/shared/components/AgentActivityTable.tsx +66 -0
  153. package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
  154. package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
  155. package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
  156. package/src/renderer/shared/constants/models.ts +91 -0
  157. package/src/renderer/shared/platform.ts +82 -0
  158. package/src/renderer/shared/types.ts +140 -0
  159. package/src/renderer/shared/utils/agentActivity.ts +187 -0
  160. package/src/renderer/shared/utils/approval.ts +1 -0
  161. package/src/renderer/shared/utils/approval.tsx +64 -0
  162. package/src/renderer/shared/utils/markdown.tsx +347 -0
  163. package/src/renderer/shared/utils/providers.ts +36 -0
  164. package/src/renderer/shared/utils/speech.ts +216 -0
  165. package/src/renderer/shared/utils/ui.ts +147 -0
  166. package/src/renderer/src/agentProgress.ts +11 -39
  167. package/src/renderer/src/calculator.ts +3 -46
  168. package/src/renderer/src/components/ChatPanel.tsx +230 -1202
  169. package/src/renderer/src/components/ImageStudioPanel.tsx +11 -1
  170. package/src/renderer/src/components/MintDashboard.tsx +63 -171
  171. package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
  172. package/src/renderer/src/components/Settings/AgentsTab.tsx +362 -0
  173. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
  174. package/src/renderer/src/components/Settings/PluginsTab.tsx +221 -41
  175. package/src/renderer/src/components/SettingsWindow.tsx +54 -12
  176. package/src/renderer/src/components/WorkspacePanel.tsx +66 -9
  177. package/src/renderer/src/css/image-studio.css +6 -0
  178. package/src/renderer/src/css/styles.css +148 -11
  179. package/src/renderer/src/index.css +12 -1
  180. package/src/renderer/src/tauri.ts +169 -143
  181. package/src/renderer/src-web/agentProgress.ts +11 -39
  182. package/src/renderer/src-web/calculator.ts +3 -46
  183. package/src/renderer/src-web/components/ChatPanel.tsx +223 -1199
  184. package/src/renderer/src-web/components/ImageStudioPanel.tsx +11 -1
  185. package/src/renderer/src-web/components/MintDashboard.tsx +31 -172
  186. package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
  187. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +362 -0
  188. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
  189. package/src/renderer/src-web/components/Settings/PluginsTab.tsx +221 -41
  190. package/src/renderer/src-web/components/SettingsWindow.tsx +54 -12
  191. package/src/renderer/src-web/css/image-studio.css +6 -0
  192. package/src/renderer/src-web/css/styles.css +184 -12
  193. package/src/renderer/src-web/index.css +12 -1
  194. package/src/renderer/src-web/tauri.ts +204 -104
  195. package/src-tauri/src/browser.rs +1 -141
  196. package/src-tauri/src/headless.rs +3 -2
  197. package/src-tauri/src/lib.rs +254 -59
  198. package/src-tauri/src/webhooks.rs +1 -0
  199. package/src-tauri/src/workflows.rs +57 -4
  200. package/src-tauri/tauri.conf.json +1 -1
@@ -79,10 +79,24 @@ pub struct MintConfig {
79
79
  pub blocked_paths: Vec<PathBuf>,
80
80
  pub blocked_file_names: Vec<String>,
81
81
  pub disabled_tools: Vec<String>,
82
+ pub agents: Vec<AgentConfig>,
83
+ pub enable_agent_collaboration: bool,
82
84
  #[serde(flatten)]
83
85
  pub extra: BTreeMap<String, Value>,
84
86
  }
85
87
 
88
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
89
+ #[serde(rename_all = "camelCase")]
90
+ pub struct AgentConfig {
91
+ pub id: String,
92
+ pub name: String,
93
+ pub provider: String,
94
+ pub model: String,
95
+ pub api_key: Option<String>,
96
+ pub system_instruction: String,
97
+ pub enabled: bool,
98
+ }
99
+
86
100
  impl Default for MintConfig {
87
101
  fn default() -> Self {
88
102
  let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
@@ -142,12 +156,59 @@ impl Default for MintConfig {
142
156
  ],
143
157
  blocked_file_names: vec![".env".into(), "id_rsa".into(), "id_ed25519".into()],
144
158
  disabled_tools: Vec::new(),
159
+ agents: default_agents(),
160
+ enable_agent_collaboration: false,
145
161
  extra: runtime_extra_defaults(),
146
162
  }
147
163
  }
148
164
  }
149
165
 
166
+ fn default_agents() -> Vec<AgentConfig> {
167
+ vec![
168
+ AgentConfig {
169
+ id: "planner".into(),
170
+ name: "Planner".into(),
171
+ provider: "gemini".into(),
172
+ model: "gemini-2.5-flash".into(),
173
+ api_key: None,
174
+ system_instruction: "You are the Planner agent. Your task is to analyze the user request, inspect the workspace structure, and design a step-by-step implementation plan. Create or update the implementation_plan.md file to document your proposal.".into(),
175
+ enabled: true,
176
+ },
177
+ AgentConfig {
178
+ id: "coder".into(),
179
+ name: "Coder".into(),
180
+ provider: "gemini".into(),
181
+ model: "gemini-2.5-flash".into(),
182
+ api_key: None,
183
+ system_instruction: "You are the Coder agent. Your task is to implement the changes specified in the approved implementation plan. Read relevant files, write clean and efficient code, and run terminal commands to build or verify. Stay focused on execution.".into(),
184
+ enabled: true,
185
+ },
186
+ AgentConfig {
187
+ id: "reviewer".into(),
188
+ name: "Reviewer".into(),
189
+ provider: "gemini".into(),
190
+ model: "gemini-2.5-flash".into(),
191
+ api_key: None,
192
+ system_instruction: "You are the Reviewer agent. Your task is to verify the code modifications made by the Coder. Run the automated tests, check lint errors, and ensure the implementation is correct and complete.".into(),
193
+ enabled: true,
194
+ },
195
+ ]
196
+ }
197
+
150
198
  impl MintConfig {
199
+ pub fn active_model(&self) -> &str {
200
+ match self.ai_provider.as_str() {
201
+ "anthropic" => &self.anthropic_model,
202
+ "openai" => &self.openai_model,
203
+ "openrouter" => &self.openrouter_model,
204
+ "deepseek" => &self.deepseek_model,
205
+ "huggingface" => &self.hf_model,
206
+ "local_openai" => &self.local_model_name,
207
+ "ollama" => &self.ollama_model,
208
+ _ => &self.gemini_model,
209
+ }
210
+ }
211
+
151
212
  pub fn available_providers(&self) -> Vec<&'static str> {
152
213
  let mut providers = Vec::new();
153
214
  if has_value(&self.anthropic_api_key) {
@@ -208,7 +269,20 @@ pub fn set_config_value(key: &str, value: Value) -> Result<MintConfig, ConfigErr
208
269
 
209
270
  fn load_config_from(path: &Path) -> Result<MintConfig, ConfigError> {
210
271
  if !path.exists() {
211
- let config = MintConfig::default();
272
+ let mut config = MintConfig::default();
273
+ if which("gk") {
274
+ let mut servers = BTreeMap::new();
275
+ servers.insert(
276
+ "gitkraken".to_string(),
277
+ serde_json::json!({
278
+ "command": "gk",
279
+ "args": ["mcp"]
280
+ }),
281
+ );
282
+ config
283
+ .extra
284
+ .insert("mcpServers".into(), serde_json::to_value(servers).unwrap());
285
+ }
212
286
  save_config_to(path, &config)?;
213
287
  return Ok(config);
214
288
  }
@@ -225,6 +299,32 @@ fn load_config_from(path: &Path) -> Result<MintConfig, ConfigError> {
225
299
  for (key, value) in runtime_extra_defaults() {
226
300
  config.extra.entry(key).or_insert(value);
227
301
  }
302
+
303
+ let has_gitkraken = config
304
+ .extra
305
+ .get("mcpServers")
306
+ .and_then(|v| v.get("gitkraken"))
307
+ .is_some();
308
+ if !has_gitkraken && which("gk") {
309
+ let mut servers: BTreeMap<String, Value> = config
310
+ .extra
311
+ .get("mcpServers")
312
+ .cloned()
313
+ .and_then(|v| serde_json::from_value(v).ok())
314
+ .unwrap_or_default();
315
+ servers.insert(
316
+ "gitkraken".to_string(),
317
+ serde_json::json!({
318
+ "command": "gk",
319
+ "args": ["mcp"]
320
+ }),
321
+ );
322
+ config
323
+ .extra
324
+ .insert("mcpServers".into(), serde_json::to_value(servers).unwrap());
325
+ let _ = save_config_to(path, &config);
326
+ }
327
+
228
328
  Ok(config)
229
329
  }
230
330
 
@@ -317,6 +417,21 @@ fn runtime_extra_defaults() -> BTreeMap<String, Value> {
317
417
  .expect("runtime config defaults must be a JSON object")
318
418
  }
319
419
 
420
+ pub fn which(cmd: &str) -> bool {
421
+ #[cfg(windows)]
422
+ let check_cmd = "where";
423
+ #[cfg(not(windows))]
424
+ let check_cmd = "which";
425
+
426
+ std::process::Command::new(check_cmd)
427
+ .arg(cmd)
428
+ .stdout(std::process::Stdio::null())
429
+ .stderr(std::process::Stdio::null())
430
+ .status()
431
+ .map(|status| status.success())
432
+ .unwrap_or(false)
433
+ }
434
+
320
435
  #[cfg(test)]
321
436
  mod tests {
322
437
  use super::*;
@@ -384,4 +499,18 @@ mod tests {
384
499
  );
385
500
  assert_eq!(config.extra["allowedMcpTools"], serde_json::json!({}));
386
501
  }
502
+
503
+ #[test]
504
+ fn test_gitkraken_auto_detection() {
505
+ let path = std::env::temp_dir().join("mint-test-config.json");
506
+ let _ = std::fs::remove_file(&path);
507
+
508
+ let initial_config = MintConfig::default();
509
+ save_config_to(&path, &initial_config).unwrap();
510
+
511
+ let loaded = load_config_from(&path).unwrap();
512
+ assert!(loaded.theme.len() > 0);
513
+
514
+ let _ = std::fs::remove_file(&path);
515
+ }
387
516
  }
@@ -1,6 +1,7 @@
1
1
  #![recursion_limit = "256"]
2
2
 
3
3
  pub mod agent_loop;
4
+ pub mod browser;
4
5
  pub mod chat;
5
6
  pub mod code_tools;
6
7
  pub mod config;
@@ -29,6 +30,10 @@ pub use chat::{
29
30
  stream_chat_with_fallback,
30
31
  };
31
32
 
33
+ pub use browser::{
34
+ BrowserTab, click, is_browser_running, list_tabs, navigate, read_page_text,
35
+ spawn_automation_browser, type_text,
36
+ };
32
37
  pub use code_tools::{
33
38
  AppliedCodeEdit, CodeEdit, CodeEditPreview, CodeEditProposal, CodeFile, CodeInspectionError,
34
39
  CodePatchHunk, CodePlan, CodeSearchHit, RepositorySummary, apply_code_edits, build_code_patch,
@@ -86,3 +91,17 @@ pub use channels::start_channels;
86
91
 
87
92
  pub static HTTP_CLIENT: std::sync::LazyLock<reqwest::Client> =
88
93
  std::sync::LazyLock::new(reqwest::Client::new);
94
+
95
+ pub static ACTIVE_AGENTS: std::sync::LazyLock<std::sync::Mutex<std::collections::HashMap<String, tokio::task::AbortHandle>>> =
96
+ std::sync::LazyLock::new(|| std::sync::Mutex::new(std::collections::HashMap::new()));
97
+
98
+ pub fn cancel_agent(chat_id: &str) -> bool {
99
+ let mut agents = ACTIVE_AGENTS.lock().unwrap();
100
+ if let Some(handle) = agents.remove(chat_id) {
101
+ handle.abort();
102
+ true
103
+ } else {
104
+ false
105
+ }
106
+ }
107
+