--- ../Rack/src/app/MenuBar.cpp	2022-09-21 19:49:12.198540676 +0100
+++ MenuBar.cpp	2022-12-30 14:50:06.801891005 +0000
@@ -1,8 +1,33 @@
+/*
+ * DISTRHO Cardinal Plugin
+ * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of
+ * the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * For a full copy of the GNU General Public License see the LICENSE file.
+ */
+
+/**
+ * This file is an edited version of VCVRack's app/MenuBar.cpp
+ * Copyright (C) 2016-2021 VCV.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ */
+
 #include <thread>
 #include <utility>
 
-#include <osdialog.h>
-
 #include <app/MenuBar.hpp>
 #include <app/TipWindow.hpp>
 #include <widget/OpaqueWidget.hpp>
@@ -15,6 +40,7 @@
 #include <ui/ProgressBar.hpp>
 #include <ui/Label.hpp>
 #include <engine/Engine.hpp>
+#include <widget/FramebufferWidget.hpp>
 #include <window/Window.hpp>
 #include <asset.hpp>
 #include <context.hpp>
@@ -25,8 +51,28 @@
 #include <patch.hpp>
 #include <library.hpp>
 
+#include "../CardinalCommon.hpp"
+#include "../CardinalRemote.hpp"
+#include "DistrhoPlugin.hpp"
+#include "DistrhoStandaloneUtils.hpp"
+
+#ifdef HAVE_LIBLO
+# include <lo/lo.h>
+#endif
+
+void switchDarkMode(bool darkMode);
 
 namespace rack {
+namespace asset {
+std::string patchesPath();
+}
+namespace engine {
+void Engine_setRemoteDetails(Engine*, remoteUtils::RemoteDetails*);
+}
+namespace plugin {
+void updateStaticPluginsDarkMode();
+}
+
 namespace app {
 namespace menuBar {
 
@@ -48,79 +94,160 @@
 };
 
 
-struct NotificationIcon : widget::Widget {
-	void draw(const DrawArgs& args) override {
-		nvgBeginPath(args.vg);
-		float radius = 4;
-		nvgCircle(args.vg, radius, radius, radius);
-		nvgFillColor(args.vg, nvgRGBf(1.0, 0.0, 0.0));
-		nvgFill(args.vg);
-		nvgStrokeColor(args.vg, nvgRGBf(0.5, 0.0, 0.0));
-		nvgStroke(args.vg);
-	}
-};
-
-
 ////////////////////
 // File
 ////////////////////
 
 
 struct FileButton : MenuButton {
+	const bool isStandalone;
+#if ! CARDINAL_VARIANT_MINI
+	std::vector<std::string> demoPatches;
+#endif
+
+	FileButton(const bool standalone)
+		: MenuButton(),  isStandalone(standalone)
+	{
+#if ! CARDINAL_VARIANT_MINI
+		const std::string patchesDir = asset::patchesPath() + DISTRHO_OS_SEP_STR "examples";
+
+		if (system::isDirectory(patchesDir))
+		{
+			demoPatches = system::getEntries(patchesDir);
+			std::sort(demoPatches.begin(), demoPatches.end(), [](const std::string& a, const std::string& b){
+				return string::lowercase(a) < string::lowercase(b);
+			});
+		}
+#endif
+	}
+
 	void onAction(const ActionEvent& e) override {
 		ui::Menu* menu = createMenu();
 		menu->cornerFlags = BND_CORNER_TOP;
 		menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
 
-		menu->addChild(createMenuItem("New", RACK_MOD_CTRL_NAME "+N", []() {
-			APP->patch->loadTemplateDialog();
+#ifndef DISTRHO_OS_WASM
+		const char* const NewShortcut = RACK_MOD_CTRL_NAME "+N";
+#else
+		const char* const NewShortcut = "";
+#endif
+		menu->addChild(createMenuItem("New", NewShortcut, []() {
+			patchUtils::loadTemplateDialog();
 		}));
 
-		menu->addChild(createMenuItem("Open", RACK_MOD_CTRL_NAME "+O", []() {
-			APP->patch->loadDialog();
+#if ! CARDINAL_VARIANT_MINI
+#ifndef DISTRHO_OS_WASM
+		menu->addChild(createMenuItem("Open / Import...", RACK_MOD_CTRL_NAME "+O", []() {
+			patchUtils::loadDialog();
 		}));
 
-		menu->addChild(createSubmenuItem("Open recent", "", [](ui::Menu* menu) {
-			for (const std::string& path : settings::recentPatchPaths) {
-				std::string name = system::getStem(path);
-				menu->addChild(createMenuItem(name, "", [=]() {
-					APP->patch->loadPathDialog(path);
-				}));
-			}
-		}, settings::recentPatchPaths.empty()));
-
 		menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() {
-			APP->patch->saveDialog();
+			// NOTE: will do nothing if path is empty, intentionally
+			patchUtils::saveDialog(APP->patch->path);
+		}, APP->patch->path.empty()));
+
+		menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() {
+			patchUtils::saveAsDialog();
+		}));
+#else
+		menu->addChild(createMenuItem("Import patch...", RACK_MOD_CTRL_NAME "+O", []() {
+			patchUtils::loadDialog();
 		}));
 
-		menu->addChild(createMenuItem("Save as", RACK_MOD_CTRL_NAME "+Shift+S", []() {
-			APP->patch->saveAsDialog();
+		menu->addChild(createMenuItem("Import selection...", "", [=]() {
+			patchUtils::loadSelectionDialog();
+		}, false, true));
+
+		menu->addChild(createMenuItem("Save and download compressed", RACK_MOD_CTRL_NAME "+Shift+S", []() {
+			patchUtils::saveAsDialog();
 		}));
 
-		menu->addChild(createMenuItem("Save a copy", "", []() {
-			APP->patch->saveAsDialog(false);
+		menu->addChild(createMenuItem("Save and download uncompressed", "", []() {
+			patchUtils::saveAsDialogUncompressed();
 		}));
+#endif
+#endif
 
 		menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() {
-			APP->patch->revertDialog();
-		}, APP->patch->path == ""));
+			patchUtils::revertDialog();
+		}, APP->patch->path.empty()));
 
-		menu->addChild(createMenuItem("Overwrite template", "", []() {
-			APP->patch->saveTemplateDialog();
-		}));
+#if defined(HAVE_LIBLO) && ! CARDINAL_VARIANT_MINI
+		menu->addChild(new ui::MenuSeparator);
+
+		remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote();
+
+		if (remoteDetails != nullptr && remoteDetails->connected) {
+			menu->addChild(createMenuItem("Deploy to MOD", "F7", [remoteDetails]() {
+				remoteUtils::sendFullPatchToRemote(remoteDetails);
+			}));
 
+			menu->addChild(createCheckMenuItem("Auto deploy to MOD", "",
+				[remoteDetails]() {return remoteDetails->autoDeploy;},
+				[remoteDetails]() {
+					remoteDetails->autoDeploy = !remoteDetails->autoDeploy;
+					Engine_setRemoteDetails(APP->engine, remoteDetails->autoDeploy ? remoteDetails : nullptr);
+				}
+			));
+		} else {
+			menu->addChild(createMenuItem("Connect to MOD", "", []() {
+				DISTRHO_SAFE_ASSERT(remoteUtils::connectToRemote());
+			}));
+		}
+#endif
+
+#if ! CARDINAL_VARIANT_MINI
+#ifndef DISTRHO_OS_WASM
 		menu->addChild(new ui::MenuSeparator);
 
 		// Load selection
-		menu->addChild(createMenuItem("Import selection", "", [=]() {
-			APP->scene->rack->loadSelectionDialog();
+		menu->addChild(createMenuItem("Import selection...", "", [=]() {
+			patchUtils::loadSelectionDialog();
 		}, false, true));
 
-		menu->addChild(new ui::MenuSeparator);
-
-		menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() {
-			APP->window->close();
+		menu->addChild(createMenuItem("Export uncompressed json...", "", []() {
+			patchUtils::saveAsDialogUncompressed();
 		}));
+#endif
+#endif
+
+#if ! CARDINAL_VARIANT_MINI
+		if (!demoPatches.empty())
+		{
+			menu->addChild(new ui::MenuSeparator);
+
+			menu->addChild(createSubmenuItem("Open Demo / Example project", "", [=](ui::Menu* const menu) {
+				for (std::string path : demoPatches) {
+					std::string label = system::getStem(path);
+
+					for (size_t i=0, len=label.size(); i<len; ++i) {
+						if (label[i] == '_')
+							label[i] = ' ';
+					}
+
+					menu->addChild(createMenuItem(label, "", [path]() {
+						patchUtils::loadPathDialog(path, true);
+					}));
+				}
+
+				menu->addChild(new ui::MenuSeparator);
+
+				menu->addChild(createMenuItem("Open PatchStorage.com for more patches", "", []() {
+					patchUtils::openBrowser("https://patchstorage.com/platform/cardinal/");
+				}));
+			}));
+		}
+#endif
+
+#ifndef DISTRHO_OS_WASM
+		if (isStandalone) {
+			menu->addChild(new ui::MenuSeparator);
+
+			menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() {
+				APP->window->close();
+			}));
+		}
+#endif
 	}
 };
 
@@ -166,7 +293,7 @@
 
 		menu->addChild(new ui::MenuSeparator);
 
-		APP->scene->rack->appendSelectionContextMenu(menu);
+		patchUtils::appendSelectionContextMenu(menu);
 	}
 };
 
@@ -256,7 +383,7 @@
 		return settings::cableTension;
 	}
 	float getDefaultValue() override {
-		return 0.5;
+		return 0.75;
 	}
 	float getDisplayValue() override {
 		return getValue() * 100;
@@ -393,36 +520,37 @@
 };
 
 
+static void setAllFramebufferWidgetsDirty(widget::Widget* const widget)
+{
+	for (widget::Widget* child : widget->children)
+	{
+		if (widget::FramebufferWidget* const fbw = dynamic_cast<widget::FramebufferWidget*>(child))
+		{
+			fbw->setDirty();
+			break;
+		}
+		setAllFramebufferWidgetsDirty(child);
+	}
+}
+
+
 struct ViewButton : MenuButton {
 	void onAction(const ActionEvent& e) override {
 		ui::Menu* menu = createMenu();
 		menu->cornerFlags = BND_CORNER_TOP;
 		menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
 
-		menu->addChild(createMenuLabel("Window"));
-
-		bool fullscreen = APP->window->isFullScreen();
-		std::string fullscreenText = "F11";
-		if (fullscreen)
-			fullscreenText += " " CHECKMARK_STRING;
-		menu->addChild(createMenuItem("Fullscreen", fullscreenText, [=]() {
-			APP->window->setFullScreen(!fullscreen);
-		}));
+		menu->addChild(createMenuLabel("Appearance"));
 
-		double frameRate = APP->window->getMonitorRefreshRate() / settings::frameSwapInterval;
-		menu->addChild(createSubmenuItem("Frame rate", string::f("%.0f Hz", frameRate), [=](ui::Menu* menu) {
-			for (int i = 1; i <= 6; i++) {
-				double frameRate = APP->window->getMonitorRefreshRate() / i;
-				menu->addChild(createCheckMenuItem(string::f("%.0f Hz", frameRate), "",
-					[=]() {return settings::frameSwapInterval == i;},
-					[=]() {settings::frameSwapInterval = i;}
-				));
-			}
+		std::string darkModeText;
+		if (settings::darkMode)
+			darkModeText = CHECKMARK_STRING;
+		menu->addChild(createMenuItem("Dark Mode", darkModeText, []() {
+			switchDarkMode(!settings::darkMode);
+			plugin::updateStaticPluginsDarkMode();
+			setAllFramebufferWidgetsDirty(APP->scene);
 		}));
 
-		menu->addChild(new ui::MenuSeparator);
-		menu->addChild(createMenuLabel("Appearance"));
-
 		menu->addChild(createBoolPtrMenuItem("Show tooltips", "", &settings::tooltips));
 
 		ZoomSlider* zoomSlider = new ZoomSlider;
@@ -446,9 +574,18 @@
 		menu->addChild(haloBrightnessSlider);
 
 		menu->addChild(new ui::MenuSeparator);
+		menu->addChild(createMenuLabel("Module dragging"));
+
+		menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules));
+
+		menu->addChild(createBoolPtrMenuItem("Auto-squeeze modules when dragging", "", &settings::squeezeModules));
+
+		menu->addChild(new ui::MenuSeparator);
 		menu->addChild(createMenuLabel("Parameters"));
 
+#ifdef DISTRHO_OS_WASM
 		menu->addChild(createBoolPtrMenuItem("Lock cursor while dragging", "", &settings::allowCursorLock));
+#endif
 
 		static const std::vector<std::string> knobModeLabels = {
 			"Linear",
@@ -473,11 +610,34 @@
 		menu->addChild(knobScrollSensitivitySlider);
 
 		menu->addChild(new ui::MenuSeparator);
-		menu->addChild(createMenuLabel("Module dragging"));
+		menu->addChild(createMenuLabel("Window"));
+
+#ifdef DISTRHO_OS_WASM
+		const bool fullscreen = APP->window->isFullScreen();
+		std::string rightText = "F11";
+		if (fullscreen)
+			rightText += " " CHECKMARK_STRING;
+		menu->addChild(createMenuItem("Fullscreen", rightText, [=]() {
+			APP->window->setFullScreen(!fullscreen);
+		}));
+#endif
 
-		menu->addChild(createBoolPtrMenuItem("Lock positions", "", &settings::lockModules));
+		menu->addChild(createBoolPtrMenuItem("Invert zoom", "", &settings::invertZoom));
 
-		menu->addChild(createBoolPtrMenuItem("Auto-squeeze algorithm (experimental)", "", &settings::squeezeModules));
+		static const std::vector<std::string> rateLimitLabels = {
+			"None",
+			"2x",
+			"4x",
+		};
+		static const std::vector<int> rateLimits = {0, 1, 2};
+		menu->addChild(createSubmenuItem("Update rate limit", rateLimitLabels[settings::rateLimit], [=](ui::Menu* menu) {
+			for (int rateLimit : rateLimits) {
+				menu->addChild(createCheckMenuItem(rateLimitLabels[rateLimit], "",
+					[=]() {return settings::rateLimit == rateLimit;},
+					[=]() {settings::rateLimit = rateLimit;}
+				));
+			}
+		}));
 	}
 };
 
@@ -487,47 +647,6 @@
 ////////////////////
 
 
-struct SampleRateItem : ui::MenuItem {
-	ui::Menu* createChildMenu() override {
-		ui::Menu* menu = new ui::Menu;
-
-		// Auto sample rate
-		std::string rightText;
-		if (settings::sampleRate == 0) {
-			float sampleRate = APP->engine->getSampleRate();
-			rightText += string::f("(%g kHz) ", sampleRate / 1000.f);
-		}
-		menu->addChild(createCheckMenuItem("Auto", rightText,
-			[=]() {return settings::sampleRate == 0;},
-			[=]() {settings::sampleRate = 0;}
-		));
-
-		// Power-of-2 oversample times 44.1kHz or 48kHz
-		for (int i = -2; i <= 4; i++) {
-			for (int j = 0; j < 2; j++) {
-				float oversample = std::pow(2.f, i);
-				float sampleRate = (j == 0) ? 44100.f : 48000.f;
-				sampleRate *= oversample;
-
-				std::string text = string::f("%g kHz", sampleRate / 1000.f);
-				std::string rightText;
-				if (oversample > 1.f) {
-					rightText += string::f("(%.0fx)", oversample);
-				}
-				else if (oversample < 1.f) {
-					rightText += string::f("(1/%.0fx)", 1.f / oversample);
-				}
-				menu->addChild(createCheckMenuItem(text, rightText,
-					[=]() {return settings::sampleRate == sampleRate;},
-					[=]() {settings::sampleRate = sampleRate;}
-				));
-			}
-		}
-		return menu;
-	}
-};
-
-
 struct EngineButton : MenuButton {
 	void onAction(const ActionEvent& e) override {
 		ui::Menu* menu = createMenu();
@@ -541,268 +660,46 @@
 			settings::cpuMeter ^= true;
 		}));
 
-		menu->addChild(createMenuItem<SampleRateItem>("Sample rate", RIGHT_ARROW));
-
-		menu->addChild(createSubmenuItem("Threads", string::f("%d", settings::threadCount), [=](ui::Menu* menu) {
-			// BUG This assumes SMT is enabled.
-			int cores = system::getLogicalCoreCount() / 2;
-
-			for (int i = 1; i <= 2 * cores; i++) {
+		if (isUsingNativeAudio()) {
+			if (supportsAudioInput()) {
+				const bool enabled = isAudioInputEnabled();
 				std::string rightText;
-				if (i == cores)
-					rightText += "(most modules)";
-				else if (i == 1)
-					rightText += "(lowest CPU usage)";
-				menu->addChild(createCheckMenuItem(string::f("%d", i), rightText,
-					[=]() {return settings::threadCount == i;},
-					[=]() {settings::threadCount = i;}
-				));
-			}
-		}));
-	}
-};
-
-
-////////////////////
-// Plugins
-////////////////////
-
-
-struct AccountPasswordField : ui::PasswordField {
-	ui::MenuItem* logInItem;
-	void onAction(const ActionEvent& e) override {
-		logInItem->doAction();
-	}
-};
-
-
-struct LogInItem : ui::MenuItem {
-	ui::TextField* emailField;
-	ui::TextField* passwordField;
-
-	void onAction(const ActionEvent& e) override {
-		std::string email = emailField->text;
-		std::string password = passwordField->text;
-		std::thread t([=] {
-			library::logIn(email, password);
-			library::checkUpdates();
-		});
-		t.detach();
-		e.unconsume();
-	}
-
-	void step() override {
-		text = "Log in";
-		rightText = library::loginStatus;
-		MenuItem::step();
-	}
-};
-
-
-struct SyncUpdatesItem : ui::MenuItem {
-	void step() override {
-		if (library::updateStatus != "") {
-			text = library::updateStatus;
-		}
-		else if (library::isSyncing) {
-			text = "Updating...";
-		}
-		else if (!library::hasUpdates()) {
-			text = "Up-to-date";
-		}
-		else {
-			text = "Update all";
-		}
-
-		disabled = library::isSyncing || !library::hasUpdates();
-		MenuItem::step();
-	}
-
-	void onAction(const ActionEvent& e) override {
-		std::thread t([=] {
-			library::syncUpdates();
-		});
-		t.detach();
-		e.unconsume();
-	}
-};
-
-
-struct SyncUpdateItem : ui::MenuItem {
-	std::string slug;
-
-	void setUpdate(const std::string& slug) {
-		this->slug = slug;
-
-		auto it = library::updateInfos.find(slug);
-		if (it == library::updateInfos.end())
-			return;
-		library::UpdateInfo update = it->second;
-
-		text = update.name;
-	}
-
-	ui::Menu* createChildMenu() override {
-		auto it = library::updateInfos.find(slug);
-		if (it == library::updateInfos.end())
-			return NULL;
-		library::UpdateInfo update = it->second;
-
-		if (update.changelogUrl == "")
-			return NULL;
-
-		ui::Menu* menu = new ui::Menu;
-
-		std::string changelogUrl = update.changelogUrl;
-		menu->addChild(createMenuItem("Changelog", "", [=]() {
-			system::openBrowser(changelogUrl);
-		}));
-
-		return menu;
-	}
-
-	void step() override {
-		disabled = library::isSyncing;
-
-		auto it = library::updateInfos.find(slug);
-		if (it != library::updateInfos.end()) {
-			library::UpdateInfo update = it->second;
-
-			if (update.downloaded) {
-				rightText = CHECKMARK_STRING;
-				disabled = true;
-			}
-			else if (slug == library::updateSlug) {
-				rightText = string::f("%.0f%%", library::updateProgress * 100.f);
-			}
-			else {
-				rightText = "";
-				plugin::Plugin* p = plugin::getPlugin(slug);
-				if (p) {
-					rightText += p->version + " → ";
-				}
-				rightText += update.version;
+				if (enabled)
+					rightText = CHECKMARK_STRING;
+				menu->addChild(createMenuItem("Enable Audio Input", rightText, [enabled]() {
+					if (!enabled)
+						requestAudioInput();
+				}));
 			}
-		}
 
-		MenuItem::step();
-	}
-
-	void onAction(const ActionEvent& e) override {
-		std::thread t([=] {
-			library::syncUpdate(slug);
-		});
-		t.detach();
-		e.unconsume();
-	}
-};
-
-
-struct LibraryMenu : ui::Menu {
-	LibraryMenu() {
-		refresh();
-	}
-
-	void step() override {
-		// Refresh menu when appropriate
-		if (library::refreshRequested) {
-			library::refreshRequested = false;
-			refresh();
-		}
-		Menu::step();
-	}
-
-	void refresh() {
-		setChildMenu(NULL);
-		clearChildren();
-
-		if (settings::devMode) {
-			addChild(createMenuLabel("Disabled in development mode"));
-		}
-		else if (!library::isLoggedIn()) {
-			addChild(createMenuItem("Register VCV account", "", [=]() {
-				system::openBrowser("https://vcvrack.com/login");
-			}));
-
-			ui::TextField* emailField = new ui::TextField;
-			emailField->placeholder = "Email";
-			emailField->box.size.x = 240.0;
-			addChild(emailField);
-
-			AccountPasswordField* passwordField = new AccountPasswordField;
-			passwordField->placeholder = "Password";
-			passwordField->box.size.x = 240.0;
-			passwordField->nextField = emailField;
-			emailField->nextField = passwordField;
-			addChild(passwordField);
-
-			LogInItem* logInItem = new LogInItem;
-			logInItem->emailField = emailField;
-			logInItem->passwordField = passwordField;
-			passwordField->logInItem = logInItem;
-			addChild(logInItem);
-		}
-		else {
-			addChild(createMenuItem("Log out", "", [=]() {
-				library::logOut();
-			}));
-
-			addChild(createMenuItem("Browse VCV Library", "", [=]() {
-				system::openBrowser("https://library.vcvrack.com/");
-			}));
-
-			SyncUpdatesItem* syncItem = new SyncUpdatesItem;
-			syncItem->text = "Update all";
-			addChild(syncItem);
-
-			if (!library::updateInfos.empty()) {
-				addChild(new ui::MenuSeparator);
-				addChild(createMenuLabel("Updates"));
-
-				for (auto& pair : library::updateInfos) {
-					SyncUpdateItem* updateItem = new SyncUpdateItem;
-					updateItem->setUpdate(pair.first);
-					addChild(updateItem);
-				}
+			if (supportsMIDI()) {
+				std::string rightText;
+				if (isMIDIEnabled())
+					rightText = CHECKMARK_STRING;
+				menu->addChild(createMenuItem("Enable/Reconnect MIDI", rightText, []() {
+					requestMIDI();
+				}));
 			}
-		}
-	}
-};
-
-
-struct LibraryButton : MenuButton {
-	NotificationIcon* notification;
-
-	LibraryButton() {
-		notification = new NotificationIcon;
-		addChild(notification);
-	}
-
-	void onAction(const ActionEvent& e) override {
-		ui::Menu* menu = createMenu<LibraryMenu>();
-		menu->cornerFlags = BND_CORNER_TOP;
-		menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
-		// Check for updates when menu is opened
-		std::thread t([&]() {
-			system::setThreadName("Library");
-			library::checkUpdates();
-		});
-		t.detach();
-	}
-
-	void step() override {
-		notification->box.pos = math::Vec(0, 0);
-		notification->visible = library::hasUpdates();
 
-		// Popup when updates finish downloading
-		if (library::restartRequested) {
-			library::restartRequested = false;
-			if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been downloaded. Close and re-launch Rack to load new updates.")) {
-				APP->window->close();
+			if (supportsBufferSizeChanges()) {
+				static const std::vector<uint32_t> bufferSizes = {
+					#ifdef DISTRHO_OS_WASM
+					256, 512, 1024, 2048, 4096, 8192, 16384
+					#else
+					128, 256, 512, 1024, 2048, 4096, 8192
+					#endif
+				};
+				const uint32_t currentBufferSize = getBufferSize();
+				menu->addChild(createSubmenuItem("Buffer Size", std::to_string(currentBufferSize), [=](ui::Menu* menu) {
+					for (uint32_t bufferSize : bufferSizes) {
+						menu->addChild(createCheckMenuItem(std::to_string(bufferSize), "",
+							[=]() {return currentBufferSize == bufferSize;},
+							[=]() {requestBufferSizeChange(bufferSize);}
+						));
+					}
+				}));
 			}
 		}
-
-		MenuButton::step();
 	}
 };
 
@@ -813,65 +710,23 @@
 
 
 struct HelpButton : MenuButton {
-	NotificationIcon* notification;
-
-	HelpButton() {
-		notification = new NotificationIcon;
-		addChild(notification);
-	}
-
 	void onAction(const ActionEvent& e) override {
 		ui::Menu* menu = createMenu();
 		menu->cornerFlags = BND_CORNER_TOP;
 		menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
 
-		menu->addChild(createMenuItem("Tips", "", [=]() {
-			APP->scene->addChild(tipWindowCreate());
-		}));
-
-		menu->addChild(createMenuItem("User manual", "F1", [=]() {
-			system::openBrowser("https://vcvrack.com/manual");
-		}));
-
-		menu->addChild(createMenuItem("Support", "", [=]() {
-			system::openBrowser("https://vcvrack.com/support");
+		menu->addChild(createMenuItem("Rack User manual", "F1", [=]() {
+			patchUtils::openBrowser("https://vcvrack.com/manual");
 		}));
 
-		menu->addChild(createMenuItem("VCVRack.com", "", [=]() {
-			system::openBrowser("https://vcvrack.com/");
+		menu->addChild(createMenuItem("Cardinal Project page", "", [=]() {
+			patchUtils::openBrowser("https://github.com/DISTRHO/Cardinal/");
 		}));
 
 		menu->addChild(new ui::MenuSeparator);
 
-		menu->addChild(createMenuLabel(APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION));
-
-		menu->addChild(createMenuItem("Open user folder", "", [=]() {
-			system::openDirectory(asset::user(""));
-		}));
-
-		menu->addChild(createMenuItem("Changelog", "", [=]() {
-			system::openBrowser("https://github.com/VCVRack/Rack/blob/v2/CHANGELOG.md");
-		}));
-
-		if (library::isAppUpdateAvailable()) {
-			menu->addChild(createMenuItem("Update " + APP_NAME, APP_VERSION + " → " + library::appVersion, [=]() {
-				system::openBrowser(library::appDownloadUrl);
-			}));
-		}
-		else if (!settings::autoCheckUpdates && !settings::devMode) {
-			menu->addChild(createMenuItem("Check for " + APP_NAME + " update", "", [=]() {
-				std::thread t([&]() {
-					library::checkAppUpdate();
-				});
-				t.detach();
-			}, false, true));
-		}
-	}
-
-	void step() override {
-		notification->box.pos = math::Vec(0, 0);
-		notification->visible = library::isAppUpdateAvailable();
-		MenuButton::step();
+		menu->addChild(createMenuLabel("Cardinal " + APP_EDITION + " " + CARDINAL_VERSION));
+		menu->addChild(createMenuLabel("Rack " + APP_VERSION + " Compatible"));
 	}
 };
 
@@ -910,9 +765,14 @@
 		// 	uiLastTime = time;
 		// }
 
+#if CARDINAL_VARIANT_MINI
+		text = string::f("%.1f fps", 1.0 / frameDurationAvg);
+#else
 		double meterAverage = APP->engine->getMeterAverage();
 		double meterMax = APP->engine->getMeterMax();
 		text = string::f("%.1f fps  %.1f%% avg  %.1f%% max", 1.0 / frameDurationAvg, meterAverage * 100, meterMax * 100);
+#endif
+
 		Label::step();
 	}
 };
@@ -921,7 +781,9 @@
 struct MenuBar : widget::OpaqueWidget {
 	MeterLabel* meterLabel;
 
-	MenuBar() {
+	MenuBar(const bool isStandalone)
+		: widget::OpaqueWidget()
+	{
 		const float margin = 5;
 		box.size.y = BND_WIDGET_HEIGHT + 2 * margin;
 
@@ -930,7 +792,7 @@
 		layout->spacing = math::Vec(0, 0);
 		addChild(layout);
 
-		FileButton* fileButton = new FileButton;
+		FileButton* fileButton = new FileButton(isStandalone);
 		fileButton->text = "File";
 		layout->addChild(fileButton);
 
@@ -942,13 +804,11 @@
 		viewButton->text = "View";
 		layout->addChild(viewButton);
 
+#if ! CARDINAL_VARIANT_MINI
 		EngineButton* engineButton = new EngineButton;
 		engineButton->text = "Engine";
 		layout->addChild(engineButton);
-
-		LibraryButton* libraryButton = new LibraryButton;
-		libraryButton->text = "Library";
-		layout->addChild(libraryButton);
+#endif
 
 		HelpButton* helpButton = new HelpButton;
 		helpButton->text = "Help";
@@ -984,7 +844,7 @@
 
 
 widget::Widget* createMenuBar() {
-	menuBar::MenuBar* menuBar = new menuBar::MenuBar;
+	menuBar::MenuBar* menuBar = new menuBar::MenuBar(isStandalone());
 	return menuBar;
 }
 
