diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index b2747da59e..47bf58d711 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -58,6 +58,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libfcitx5-qt-dev fcitx-libs-dev extra-cmake-modules libxkbcommon-dev sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tree sudo DEBIAN_FRONTEND=noninteractive apt-get install -y fuse libxcb-cursor-dev + python3 -m pip config set global.break-system-packages true - name: Cache Qt id: cache-qt diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index e55ffaf80f..64bdd65ec4 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -8,27 +8,31 @@ on: # Allows you to run this workflow manually from the Actions tab. workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled' + required: false + default: false env: VNOTE_VER: 3.17.0 + CMAKE_VER: 3.24.3 jobs: build: name: Build On MacOS - runs-on: macos-latest timeout-minutes: 120 strategy: + fail-fast: false matrix: config: - # - { - # name: "Build Qt 5.12", - # qt: 5.12.12 - # } - - { - name: "Build Qt 5.15", - qt: 5.15.2 - } + - name: "Build on Arm64" + os: macos-latest + arch: universal + + runs-on: ${{matrix.config.os}} steps: # Checks-out your repository under $GITHUB_WORKSPACE. @@ -44,7 +48,24 @@ jobs: - name: Install Dependencies run: | - brew install tree + brew install tree libiodbc libpq + + - name: Fix SQL + if: ${{matrix.config.arch == 'universal'}} + run: | + sudo mkdir -p /usr/local/opt/libiodbc/lib + sudo ln -s /opt/homebrew/opt/libiodbc/lib/libiodbc.2.dylib /usr/local/opt/libiodbc/lib/libiodbc.2.dylib + sudo mkdir -p /Applications/Postgres.app/Contents/Versions/14/lib + sudo ln -s /opt/homebrew/Cellar/libpq/16.3/lib/libpq.5.dylib /Applications/Postgres.app/Contents/Versions/14/lib/libpq.5.dylib + + - name: Install a fresh CMake + run: | + wget --no-verbose https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-macos-universal.tar.gz + tar xzf cmake-${CMAKE_VER}-macos-universal.tar.gz + sudo rm -f /usr/local/bin/cmake /usr/local/bin/cpack + sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-macos-universal/CMake.app/Contents/bin/cmake /usr/local/bin/cmake + sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-macos-universal/CMake.app/Contents/bin/cpack /usr/local/bin/cpack + working-directory: ${{runner.workspace}} - name: Install macdeployqtfix run: | @@ -56,17 +77,15 @@ jobs: uses: actions/cache@v1 # not v2! with: path: ../Qt - key: ${{ runner.os }}-QtCache-${{ matrix.config.qt }} + key: ${{ runner.os }}-QtCache-6.5 - name: Install Qt uses: jurplel/install-qt-action@v3 with: - version: ${{ matrix.config.qt }} + version: 6.5.3 target: desktop - modules: qtwebengine - cached: ${{ steps.cache-qt.outputs.cache-hit }} - setup-python: 'false' - + modules: 'qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat qtserialport' + cache: 'true' - name: Create Build Dir run: mkdir build @@ -75,77 +94,28 @@ jobs: - name: Configure Project run: | qmake -v - qmake CONFIG+=release ${GITHUB_WORKSPACE}/vnote.pro + cmake --version + cmake -DMACDEPLOYQTFIX_EXECUTABLE=${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" ${GITHUB_WORKSPACE} working-directory: ${{runner.workspace}}/build - name: Build Project - run: make -j4 - working-directory: ${{runner.workspace}}/build - - - name: Deploy VSyntaxHighlighting Framework - run: | - my_lib_name=VSyntaxHighlighting - my_lib_framework=${my_lib_name}.framework - my_lib_dir=./libs/vtextedit/src/libs/syntax-highlighting - frameworks_dir=./src/vnote.app/Contents/Frameworks - mkdir -p ${frameworks_dir} - cp -R ${my_lib_dir}/${my_lib_framework} ${frameworks_dir} - working-directory: ${{runner.workspace}}/build - - - name: Deploy VTextEdit Framework run: | - my_lib_name=VTextEdit - my_lib_framework=${my_lib_name}.framework - my_lib_dir=./libs/vtextedit/src/editor - frameworks_dir=./src/vnote.app/Contents/Frameworks - mkdir -p ${frameworks_dir} - cp -R ${my_lib_dir}/${my_lib_framework} ${frameworks_dir} + cmake --build . --target pack + ls -ls . + ls -ls src + mv src/vnote.dmg VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg working-directory: ${{runner.workspace}}/build - - name: Cleanup rpath - run: | - app_target=./src/vnote.app/Contents/MacOS/vnote - install_name_tool -delete_rpath ${PWD}/src/../libs/vtextedit/src/editor ${app_target} - install_name_tool -delete_rpath ${PWD}/src/../libs/vtextedit/src/libs/syntax-highlighting ${app_target} - working-directory: ${{runner.workspace}}/build - - - name: Mac Deploy - run: | - pushd src - macdeployqt vnote.app - - # Remove unused sql drivers - rm vnote.app/Contents/Plugins/sqldrivers/libqsqlodbc.dylib - rm vnote.app/Contents/Plugins/sqldrivers/libqsqlpsql.dylib - - python ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py vnote.app/Contents/MacOS/vnote $Qt5_Dir - # Fix Helpers/QtWebEngineProcess.app - pushd vnote.app/Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers - python ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess $Qt5_Dir - popd - - popd - tree ./ - working-directory: ${{runner.workspace}}/build - - - name: Package Project - run: | - mkdir -p distrib/vnote - pushd distrib/vnote - mv ../../src/vnote.app ./ - ln -s /Applications ./Applications - popd - sleep 60 - hdiutil create -srcfolder ./distrib/vnote -format UDBZ vnote-mac-x64_v${{env.VNOTE_VER}}.dmg - cp vnote-mac-x64_v${{env.VNOTE_VER}}.dmg vnote-mac-x64-qt${{matrix.config.qt}}.dmg - mv vnote-mac-x64_v${{env.VNOTE_VER}}.dmg vnote-mac-x64-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.dmg - working-directory: ${{runner.workspace}}/build + # Enable tmate debugging of manually-triggered workflows if the input option was provided + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Archive Artifacts uses: actions/upload-artifact@v2 with: - name: vnote-mac-x64-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}} - path: ${{runner.workspace}}/build/vnote-mac-x64-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.dmg + name: VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}} + path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg - name: Update Tag if: github.ref == 'refs/heads/master' @@ -158,7 +128,7 @@ jobs: uses: johnwbyrd/update-release@v1.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} - files: ${{runner.workspace}}/build/vnote-mac-x64-qt${{matrix.config.qt}}.dmg + files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg release: Continuous Build tag: continuous-build @@ -167,7 +137,7 @@ jobs: uses: ncipollo/release-action@v1.11.0 with: token: ${{ secrets.GITHUB_TOKEN }} - artifacts: ${{runner.workspace}}/build/vnote-mac-x64-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.dmg + artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg commit: master tag: v${{env.VNOTE_VER}} allowUpdates: true diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 33321eea75..1ef07933fc 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -28,14 +28,14 @@ jobs: fail-fast: false matrix: config: - - name: "Build On Win64 Qt 5.15" + - name: "Build on Win64 Qt 5.15" arch: win64_msvc2019_64 qt: 5.15.2 qt_modules: qtwebengine qt_tools: tools_opensslv3_x64 qt_major: 5 suffix: "-windows7" - - name: "Build On Win64 Qt 6.5" + - name: "Build on Win64 Qt 6.5" arch: win64_msvc2019_64 qt: 6.5.3 qt_modules: "qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat" diff --git a/.gitignore b/.gitignore index 2a5e9bc31d..50258e8d0f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ aqtinstall.log tags CMakeLists.txt.user build +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index a9be2e30ca..bb1aa80540 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,7 @@ cmake_minimum_required (VERSION 3.20) + +set(CMAKE_OSX_DEPLOYMENT_TARGET "12.1" CACHE STRING "Minimum OS X deployment version") + project(VNote VERSION 3.17.0 DESCRIPTION "A pleasant note-taking platform" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ea5eeee00..b0cde80b36 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,8 +19,11 @@ endif() # Application icon on Windows set(VX_APP_ICON_RC_WIN data/core/icons/vnote.rc) -# Application icon on macOS +# The MACOSX_BUNDLE_ICON_FILE variable is added to the Info.plist +# generated by CMake. This variable contains the .icns file name, +# without the path. set(MACOSX_BUNDLE_ICON_FILE vnote.icns) +# And the following tells CMake where to find and install the file itself. set(VX_APP_ICON_MACOS data/core/icons/vnote.icns) set_source_files_properties(${VX_APP_ICON_MACOS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") @@ -39,19 +42,18 @@ add_custom_target(lrelease DEPENDS ${VX_QM_FILES}) # TODO: VTextEdit translations list(APPEND VX_QM_FILES - data/core/translations/qdialogbuttonbox_zh_CN.qm - data/core/translations/qtbase_ja.qm - data/core/translations/qtbase_zh_CN.qm - data/core/translations/qtv_ja.qm - data/core/translations/qtv_zh_CN.qm - data/core/translations/qwebengine_zh_CN.qm + ${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qdialogbuttonbox_zh_CN.qm + ${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtbase_ja.qm + ${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtbase_zh_CN.qm + ${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtv_ja.qm + ${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtv_zh_CN.qm + ${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qwebengine_zh_CN.qm ) -install(FILES ${VX_QM_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/translations" OPTIONAL) # Resources set(VX_RESOURCE_FILES data/core/core.qrc) -qt_add_binary_resources(VX_EXTRA_RESOURCE data/extra/extra.qrc DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/vnote_extra.rcc - OPTIONS -compress 9) +set(VX_EXTRA_RESOURCE_FILES_RCC ${CMAKE_CURRENT_BINARY_DIR}/vnote_extra.rcc) +qt_add_binary_resources(VX_EXTRA_RESOURCE data/extra/extra.qrc DESTINATION ${VX_EXTRA_RESOURCE_FILES_RCC} OPTIONS -compress 9) add_executable(vnote WIN32 MACOSX_BUNDLE application.cpp application.h @@ -115,18 +117,37 @@ if(WIN32) endif() # Installation -install(TARGETS vnote - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/vnote_extra.rcc" - DESTINATION ${CMAKE_INSTALL_BINDIR}) - if (WIN32) - install(FILES "${PROJECT_SOURCE_DIR}/package/qt.conf" - DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(TARGETS vnote RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES "${PROJECT_SOURCE_DIR}/package/qt.conf" DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES ${VX_EXTRA_RESOURCE_FILES_RCC} DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES ${VX_QM_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/translations" OPTIONAL) elseif(APPLE) - + set(CMAKE_MACOSX_RPATH ON) + + # TODO: declare install for macOS if necessary. For packing, we will manually copy files into + # the src/vnote.app bundle. + + # The generated Info.plist will be overridden. + set_target_properties(vnote + PROPERTIES + MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}" + MACOSX_BUNDLE_INFO_STRING "${PROJECT_DESCRIPTION}" + MACOSX_BUNDLE_GUI_IDENTIFIER "fun.vnote.app" + MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" + MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}" + MACOSX_BUNDLE_COPYRIGHT "Distributed under LGPL-3.0 license. Copyright (c) 2024 app.vnote.fun" + ) else() + install(TARGETS vnote + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + # TODO: move them to data dir + install(FILES ${VX_EXTRA_RESOURCE_FILES_RCC} DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES ${VX_QM_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/translations" OPTIONAL) + set(desktop.path applications) set(desktop.files data/core/vnote.desktop) set(icon16.path icons/hicolor/16x16/apps) @@ -143,9 +164,9 @@ else() set(icon256.files data/core/logo/256x256/vnote.png) set(iconsvg.path icons/hicolor/scalable/apps) set(iconsvg.files data/core/logo/vnote.svg) - foreach(items IN ITEMS desktop icon16 icon32 icon48 icon64 icon128 icon256 iconsvg) - install(FILES ${CMAKE_CURRENT_LIST_DIR}/${${items}.files} - DESTINATION share/${${items}.path} + foreach(item IN ITEMS desktop icon16 icon32 icon48 icon64 icon128 icon256 iconsvg) + install(FILES ${CMAKE_CURRENT_LIST_DIR}/${${item}.files} + DESTINATION share/${${item}.path} PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) endforeach() endif() diff --git a/src/CPackMacDeployQt.cmake.in b/src/CPackMacDeployQt.cmake.in new file mode 100644 index 0000000000..dde001a2b3 --- /dev/null +++ b/src/CPackMacDeployQt.cmake.in @@ -0,0 +1,4 @@ +execute_process(COMMAND "${MACDEPLOYQT_EXECUTABLE}" ${CMAKE_CURRENT_BINARY_DIR}/vnote.app -dmg + -always-overwrite -verbose=1 + WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY} +) diff --git a/src/Packaging.cmake b/src/Packaging.cmake index f314c3eacf..2f590a737a 100644 --- a/src/Packaging.cmake +++ b/src/Packaging.cmake @@ -46,10 +46,9 @@ function(windeployqt target) COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/styles/" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/qmltooling/" COMMENT "Deploying Qt..." - DEPENDS vnote + DEPENDS vnote lrelease ) - add_dependencies(deploy lrelease) add_dependencies(pack deploy) install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/winqt/" DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL) @@ -102,13 +101,15 @@ set(CPACK_WIX_UI_DIALOG "${PROJECT_SOURCE_DIR}/package/wix_dialog.png") set(CPACK_OUTPUT_CONFIG_FILE "${CMAKE_BINARY_DIR}/BundleConfig.cmake") add_custom_target(pack - COMMAND ${CMAKE_CPACK_COMMAND} "--config" "${CMAKE_BINARY_DIR}/BundleConfig.cmake" + COMMAND ${CMAKE_CPACK_COMMAND} "--config" "${CMAKE_BINARY_DIR}/BundleConfig.cmake" "--verbose" COMMENT "Running CPACK. Please wait..." DEPENDS vnote) add_dependencies(pack lrelease) set(CPACK_GENERATOR) +set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_LIST_DIR}/data/core/logo/64x64/vnote.png") + if(WIN32) find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}" DOC "Path to the windeployqt utility") @@ -124,19 +125,41 @@ if(WIN32) windeployqt(vnote) elseif(APPLE) + # Manually copy resources. + set(VX_BUNDLE_CONTENTS_DIR $/..) + add_custom_target(deploy + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_CURRENT_LIST_DIR}/data/core/Info.plist" ${VX_BUNDLE_CONTENTS_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${VX_EXTRA_RESOURCE_FILES_RCC} ${VX_BUNDLE_CONTENTS_DIR}/Resources + COMMAND ${CMAKE_COMMAND} -E make_directory ${VX_BUNDLE_CONTENTS_DIR}/Resources/translations + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${VX_QM_FILES} ${VX_BUNDLE_CONTENTS_DIR}/Resources/translations + COMMENT "Copying resources into bundle Contents ${VX_BUNDLE_CONTENTS_DIR}" + DEPENDS vnote lrelease + ) + add_dependencies(pack deploy) + + message(STATUS "MACDeployQtExecutable: ${MACDEPLOYQT_EXECUTABLE}") + if (MACDEPLOYQT_EXECUTABLE) + message(STATUS "Package generation - MacOS - DMG") + + list(APPEND CPACK_GENERATOR External) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CPackMacDeployQt.cmake.in "${CMAKE_BINARY_DIR}/CPackExternal.cmake") + set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_BINARY_DIR}/CPackExternal.cmake") + include(InstallRequiredSystemLibraries) + endif() else() message(STATUS "LinuxDeployExecutable: ${LINUXDEPLOY_EXECUTABLE}") if(LINUXDEPLOY_EXECUTABLE) message(STATUS "Package generation - Linux - AppImage") - set(CPACK_GENERATOR "External;${CPACK_GENERATOR}") + list(APPEND CPACK_GENERATOR External) set(VX_APPIMAGE_DEST_DIR "${CPACK_PACKAGE_DIRECTORY}/_CPack_Packages/Linux/External/AppImage") set(VX_APPIMAGE_DESKTOP_FILE "${VX_APPIMAGE_DEST_DIR}${CMAKE_INSTALL_PREFIX}/share/applications/vnote.desktop") configure_file(${CMAKE_CURRENT_LIST_DIR}/CPackLinuxDeployQt.cmake.in "${CMAKE_BINARY_DIR}/CPackExternal.cmake") set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_BINARY_DIR}/CPackExternal.cmake") endif() - - set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_LIST_DIR}/data/core/logo/64x64/vnote.png") endif() include(CPack) diff --git a/src/core/configmgr.cpp b/src/core/configmgr.cpp index 047f7ffe98..104da66cbe 100644 --- a/src/core/configmgr.cpp +++ b/src/core/configmgr.cpp @@ -575,14 +575,21 @@ void ConfigMgr::initAppPrefixPath() // Support QFile("app:abc.txt"). QStringList potential_dirs; auto app_dir_path = QCoreApplication::applicationDirPath(); - qInfo() << "executable dir: " << app_dir_path; + qInfo() << "app prefix path: " << app_dir_path; potential_dirs << app_dir_path; #if defined(Q_OS_LINUX) QDir localBinDir(app_dir_path); if (localBinDir.exists("../local/bin/vnote")) { auto app_dir_path2 = localBinDir.cleanPath(localBinDir.filePath("../local/bin")); - qInfo() << "executable dir: " << app_dir_path2; + qInfo() << "app prefix path: " << app_dir_path2; + potential_dirs << app_dir_path2; + } +#elif defined(Q_OS_MACOS) + QDir localBinDir(app_dir_path); + if (localBinDir.exists("../Resources")) { + auto app_dir_path2 = localBinDir.cleanPath(localBinDir.filePath("../Resources")); + qInfo() << "app prefix path: " << app_dir_path2; potential_dirs << app_dir_path2; } #endif diff --git a/src/data/core/Info.plist b/src/data/core/Info.plist index d8e6ca65dd..7f39ee999d 100644 --- a/src/data/core/Info.plist +++ b/src/data/core/Info.plist @@ -18,26 +18,26 @@ + CFBundleName + VNote CFBundleExecutable vnote CFBundleShortVersionString - 3.17.0 + 3.17 CFBundleVersion - 3.17.0.1 + 3.17.0 NSHumanReadableCopyright - Created by VNoteX + Distributed under LGPL-3.0 license. Copyright (c) 2024 app.vnote.fun CFBundleIconFile vnote.icns CFBundleIdentifier - com.vnotex.vnote + fun.vnote.app CFBundlePackageType APPL - CFBundleSignature - ???? LSMinimumSystemVersion - 10.12 + 12.1 NOTE - Visit [GitHub VNote](https://github.com/vnotex/vnote) for details. + A pleasant note-taking platform NSPrincipalClass NSApplication NSSupportsAutomaticGraphicsSwitching