From 145d1e5fdb1d2b707f69ce3e9ac33db33f3133cb Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 9 Apr 2025 09:36:53 +0800 Subject: [PATCH] feat: support white label on windows (#7706) --- .../shared/error_page}/error_page.dart | 34 ++-- .../notification/notification_service.dart | 7 +- .../pages/settings_shortcuts_view.dart | 2 +- .../widgets/setting_appflowy_cloud.dart | 2 +- .../theme_upload_learn_more_button.dart | 2 +- .../scripts/white_label/i18n_white_label.sh | 103 ++++++++++++ .../scripts/white_label/icon_white_label.sh | 84 ++++++++++ .../white_label/resources/my_company_logo.ico | Bin 0 -> 67646 bytes .../white_label/resources/my_company_logo.png | Bin 0 -> 4121 bytes .../white_label/resources/my_company_logo.svg | 1 + frontend/scripts/white_label/white_label.sh | 118 ++++++++++++++ .../white_label/windows_white_label.sh | 151 ++++++++++++++++++ 12 files changed, 481 insertions(+), 23 deletions(-) rename frontend/appflowy_flutter/{packages/flowy_infra_ui/lib/widget => lib/shared/error_page}/error_page.dart (88%) create mode 100644 frontend/scripts/white_label/i18n_white_label.sh create mode 100644 frontend/scripts/white_label/icon_white_label.sh create mode 100644 frontend/scripts/white_label/resources/my_company_logo.ico create mode 100644 frontend/scripts/white_label/resources/my_company_logo.png create mode 100644 frontend/scripts/white_label/resources/my_company_logo.svg create mode 100644 frontend/scripts/white_label/white_label.sh create mode 100644 frontend/scripts/white_label/windows_white_label.sh diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/error_page.dart b/frontend/appflowy_flutter/lib/shared/error_page/error_page.dart similarity index 88% rename from frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/error_page.dart rename to frontend/appflowy_flutter/lib/shared/error_page/error_page.dart index d395873bd7..9661fd822a 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/widget/error_page.dart +++ b/frontend/appflowy_flutter/lib/shared/error_page/error_page.dart @@ -1,14 +1,18 @@ import 'dart:io'; +import 'package:appflowy/core/helpers/url_launcher.dart'; +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/theme_extension.dart'; +import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flowy_infra_ui/style_widget/hover.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flowy_infra_ui/widget/flowy_tooltip.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'package:flowy_infra/theme_extension.dart'; -import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/hover.dart'; -import 'package:flowy_svg/flowy_svg.dart'; -import 'package:url_launcher/url_launcher.dart'; class FlowyErrorPage extends StatelessWidget { factory FlowyErrorPage.error( @@ -86,7 +90,9 @@ class FlowyErrorPage extends StatelessWidget { Listener( behavior: HitTestBehavior.translucent, onPointerDown: (_) async { - await Clipboard.setData(ClipboardData(text: message)); + await getIt().setData( + ClipboardServiceData(plainText: message), + ); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -188,8 +194,8 @@ class StackTracePreview extends StatelessWidget { "Copy", ), useIntrinsicWidth: true, - onTap: () => Clipboard.setData( - ClipboardData(text: stackTrace), + onTap: () => getIt().setData( + ClipboardServiceData(plainText: stackTrace), ), ), ), @@ -252,18 +258,14 @@ class GitHubRedirectButton extends StatelessWidget { Widget build(BuildContext context) { return FlowyButton( leftIconSize: const Size.square(_height), - text: const FlowyText( - "AppFlowy", - ), + text: FlowyText(LocaleKeys.appName.tr()), useIntrinsicWidth: true, leftIcon: const Padding( padding: EdgeInsets.all(4.0), child: FlowySvg(FlowySvgData('login/github-mark')), ), onTap: () async { - if (await canLaunchUrl(_gitHubNewBugUri)) { - await launchUrl(_gitHubNewBugUri); - } + await afLaunchUri(_gitHubNewBugUri); }, ); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/notification/notification_service.dart b/frontend/appflowy_flutter/lib/workspace/application/notification/notification_service.dart index 5418eb2b1c..7a19e2a822 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/notification/notification_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/notification/notification_service.dart @@ -1,9 +1,8 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; - import 'package:local_notifier/local_notifier.dart'; -const _appName = "AppFlowy"; - /// Manages Local Notifications /// /// Currently supports: @@ -13,7 +12,7 @@ const _appName = "AppFlowy"; /// class NotificationService { static Future initialize() async { - await localNotifier.setup(appName: _appName); + await localNotifier.setup(appName: LocaleKeys.appName.tr()); } } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart index 3552205ba4..0d3716c7dc 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_shortcuts_view.dart @@ -7,6 +7,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_p import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/custom_paste_command.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/math_equation/math_equation_shortcut.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/toggle/toggle_block_shortcuts.dart'; +import 'package:appflowy/shared/error_page/error_page.dart'; import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart'; import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_service.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart'; @@ -21,7 +22,6 @@ import 'package:flowy_infra/size.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; -import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart index a9cb362bf3..f423156025 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart @@ -3,6 +3,7 @@ import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/env/env.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/shared/share/constants.dart'; +import 'package:appflowy/shared/error_page/error_page.dart'; import 'package:appflowy/workspace/application/settings/appflowy_cloud_setting_bloc.dart'; import 'package:appflowy/workspace/application/settings/appflowy_cloud_urls_bloc.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/_restart_app_button.dart'; @@ -19,7 +20,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart index 9e25dece0e..bdc5ef0546 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart @@ -1,12 +1,12 @@ import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/shared/error_page/error_page.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload_view.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart'; -import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flutter/material.dart'; class ThemeUploadLearnMoreButton extends StatelessWidget { diff --git a/frontend/scripts/white_label/i18n_white_label.sh b/frontend/scripts/white_label/i18n_white_label.sh new file mode 100644 index 0000000000..60152d1630 --- /dev/null +++ b/frontend/scripts/white_label/i18n_white_label.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +show_usage() { + echo "Usage: $0 [options]" + echo "Options:" + echo " --company-name Set the custom company name" + echo " --help Show this help message" + echo "" + echo "Example:" + echo " $0 --company-name \"MyCompany Ltd.\"" +} + +CUSTOM_COMPANY_NAME="" +I18N_DIR="resources/translations" + +while [[ $# -gt 0 ]]; do + case $1 in + --company-name) + CUSTOM_COMPANY_NAME="$2" + shift 2 + ;; + --help) + show_usage + exit 0 + ;; + *) + echo "Unknown option: $1" + show_usage + exit 1 + ;; + esac +done + +if [ -z "$CUSTOM_COMPANY_NAME" ]; then + echo "Error: Company name is required" + show_usage + exit 1 +fi + +if [ ! -d "$I18N_DIR" ]; then + echo "Error: Translation directory not found at $I18N_DIR" + exit 1 +fi + +echo "Replacing 'AppFlowy' with '$CUSTOM_COMPANY_NAME' in translation files..." + +if sed --version >/dev/null 2>&1; then + SED_INPLACE="-i" +else + SED_INPLACE="-i ''" +fi + +echo "Processing translation files..." +if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then + # Check if directory exists and has JSON files + if [ ! -d "$I18N_DIR" ] || [ -z "$(ls -A "$I18N_DIR"/*.json 2>/dev/null)" ]; then + echo "Error: No JSON files found in $I18N_DIR directory" + exit 1 + fi + + # Process each JSON file in the directory + for file in "$I18N_DIR"/*.json; do + echo "Updating $(basename "$file")" + # Use jq to replace AppFlowy with custom company name in values only + if command -v jq >/dev/null 2>&1; then + # Create a temporary file for the transformation + jq --arg company "$CUSTOM_COMPANY_NAME" 'walk(if type == "string" then gsub("AppFlowy"; $company) else . end)' "$file" > "${file}.tmp" + # Check if transformation was successful + if [ $? -eq 0 ]; then + mv "${file}.tmp" "$file" + else + echo "Error: Failed to process $file with jq" + rm -f "${file}.tmp" + exit 1 + fi + else + # Fallback to sed if jq is not available + # First, escape any special characters in the company name + ESCAPED_COMPANY_NAME=$(echo "$CUSTOM_COMPANY_NAME" | sed 's/[\/&]/\\&/g') + # Replace AppFlowy with the custom company name in JSON values + sed $SED_INPLACE 's/\(".*"\): *"\(.*\)AppFlowy\(.*\)"/\1: "\2'"$ESCAPED_COMPANY_NAME"'\3"/g' "$file" + if [ $? -ne 0 ]; then + echo "Error: Failed to process $file with sed" + exit 1 + fi + fi + done +else + for file in $(find "$I18N_DIR" -name "*.json" -type f); do + echo "Updating $(basename "$file")" + # Use jq to only replace values, not keys + if command -v jq >/dev/null 2>&1; then + jq 'walk(if type == "string" then gsub("AppFlowy"; "'"$CUSTOM_COMPANY_NAME"'") else . end)' "$file" > "$file.tmp" && mv "$file.tmp" "$file" + else + # Fallback to sed with a more specific pattern that targets values but not keys + sed $SED_INPLACE 's/: *"[^"]*AppFlowy[^"]*"/: "&"/g; s/: *"&"/: "'"$CUSTOM_COMPANY_NAME"'"/g' "$file" + # Fix any double colons that might have been introduced + sed $SED_INPLACE 's/: *: */: /g' "$file" + fi + done +fi + +echo "Replacement complete!" diff --git a/frontend/scripts/white_label/icon_white_label.sh b/frontend/scripts/white_label/icon_white_label.sh new file mode 100644 index 0000000000..eb45d0f02f --- /dev/null +++ b/frontend/scripts/white_label/icon_white_label.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +show_usage() { + echo "Usage: $0 [options]" + echo "Options:" + echo " --icon-path Set the path to the application icon (.svg file)" + echo " --help Show this help message" + echo "" + echo "Example:" + echo " $0 --icon-path \"/path/to/new/icon.svg\"" +} + +NEW_ICON_PATH="" +ICON_DIR="resources/flowy_icons" +ICON_NAME_NEED_REPLACE=("flowy_logo.svg" "flowy_ai_chat_logo.svg" "flowy_logo_dark_mode.svg" "flowy_logo_text.svg") + +while [[ $# -gt 0 ]]; do + case $1 in + --icon-path) + NEW_ICON_PATH="$2" + shift 2 + ;; + --help) + show_usage + exit 0 + ;; + *) + echo "Unknown option: $1" + show_usage + exit 1 + ;; + esac +done + +if [ -z "$NEW_ICON_PATH" ]; then + echo "Error: Icon path is required" + show_usage + exit 1 +fi + +if [ ! -d "$ICON_DIR" ]; then + echo "Error: Icon directory not found at $ICON_DIR" + exit 1 +fi + +echo "Replacing icon..." + +echo "Processing icon files..." +if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then + for subdir in "${ICON_DIR}"/*/; do + if [ -d "$subdir" ]; then + echo "Checking subdirectory: $(basename "$subdir")" + for file in "${subdir}"*.svg; do + if [ -f "$file" ] && [[ " ${ICON_NAME_NEED_REPLACE[@]} " =~ " $(basename "$file") " ]]; then + echo "Updating: $(basename "$subdir")/$(basename "$file")" + cp "$NEW_ICON_PATH" "$file" + if [ $? -eq 0 ]; then + echo "Successfully replaced $(basename "$file") in $(basename "$subdir") with new icon" + else + echo "Error: Failed to replace $(basename "$file") in $(basename "$subdir")" + exit 1 + fi + fi + done + fi + done +else + for file in $(find "$ICON_DIR" -name "*.svg" -type f); do + if [[ " ${ICON_NAME_NEED_REPLACE[@]} " =~ " $(basename "$file") " ]]; then + echo "Updating: $(basename "$file")" + + cp "$NEW_ICON_PATH" "$file" + + if [ $? -eq 0 ]; then + echo "Successfully replaced $(basename "$file") with new icon" + else + echo "Error: Failed to replace $(basename "$file")" + exit 1 + fi + fi + done +fi + +echo "Replacement complete!" diff --git a/frontend/scripts/white_label/resources/my_company_logo.ico b/frontend/scripts/white_label/resources/my_company_logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..c922a6b36d45bbab0da4d142aa63e53757a04eed GIT binary patch literal 67646 zcmeHQ+mB?&U9Rrw*|}`&>?O7zVteP(b4eCy1r|5Ic#`$*&h&K8+F-{-2rVLnyr3W< zUf^Zs!2wG=#1evljFbh%iHQmR16Wud>|iB4geZ_G3c*em*;xcpmIdr(cbo64s;~R> znbUpF={`Nv(^F^l+d6eF^}Bq(y8NoD&lpp}znvX}|L4r=bjjRrj9CTY5--~8JmuN{ zJnG2h!~eko!2-bo!2-bo!2-bo!2-bo!2-bo!2-bo!2-bo<7I(bwPH4_vu1M%RGoI` z)frO*)t5lHcWz;Fyp#^w0B>SVoTn|}d;(Mfl|AvC*X(mlx2jXYa${hD`hvsWV=KHZ z&QdRed&m9y{EQw6=>fFB)*|XW+VxEm>Uw=~wzR%7MT{?i{uuN(p8f>sdVOgc_hw5t z=QFO&SeZV6ng@|B_O{KpII3-b2)O<&s0q63)9Cr%;o6)V8=yac|B!0I2M&}&#jt1J z&bqt;dJ{yv|IgEHq$K5e@B5%t5Z|c;A2?9Zn`G=~iT(FL!k%do(GPh28t4O{-~$H; z{tVc&t|#Fhy{2b^4;()12LkrN2M&M_6x81f>uzc23r5fv_6U3VMDT$T z@__=ezk_&yB%FJt!3Rdb2MWfXbAY7a10#TKng|7Aue$slU{7Mb*R@`4K=e7z4}^Kb zG|&pVKLqUm4eLS+nBScTj>IyGZFV163iE^oLEN>NAz$#R6d|+_giYQ?>_Uw~~i@oFy zJ|Ii%Pb%!|+H=Enab_9gGVR+byxzw(4L%UJ$lm04VZVqzd2xCP*lWMu(6N`M!3SiC zy-7z2dubeeK$aM8I*Qm!wKQrbB*O7VlPdC56BYv(s96E8V4VcC9}zQA zLHjbdlTG6Er-Bcp+lWt$Blglf_<$^toQ^a0(m42lEYX=B2JEGA@BvxErHuuz>X7^LzYv;`(k2cKHDLFIpX_OFw(dzwh6t z4%(kMa^E8o_N}>{HunAQ-RRWY*)f;iaqRIOu<{dgQ}lsPg5L6Y==Wd5hSxrM?#HG| zc$eS*;W-J*cN7$J8yT|4?CPT+wZ9cx;n)UEw4)yotjb!Yj zm?xbV=L2K9w|vjA&+*;GIQFP}N%cMn@1$$N2f9?c1A#rqZf*=0ZS#|G?sYBrK+FdY z0``bOO6%_Xy@OyczX(3yePF}IAFcKKQJB|@xA_HQAABHSf9QRH@jT)ek(@ z{T|hOVXw4jUr--pe!$tc_}Fkcd+ZeJ>TNdO=6gRF7wq-jC&v9e7tX!Xm=7G~93kfl zZT%QYAIK3yMSVbjGsWx1xijiG2=<%<@A*62DF38$`?nn0ZBd#1J?8dk|GZ;8&HKKK zcS*|k!d~}UK7cjFGS-hed;oDrlIV%ocFhw~w?+G%dfJNo`uW}4s-1Ug&pBD&OcbEY z#aR1V1oqdU%LiI=X>KQnZ{mIRK};?7eU9^0?`hv(0{tS0=Xu%zeJB3qt?H5TMz!E} zV6ZhKXKQCK_cttX{`f-nzh?wn$kuAxs<`&1w^Lr9?5y{`>^$@NN%ejm>p768pL#F( ze=VZt$GvWOd|-I(fU6%|0~w?p;CRB;70hEJo$XTFJ1?)V;90k3?$*JpGw(w=-}3g6 z->WU`mdeJIf7e>hW{x#%uS3fpF~47`b+-9gWn#UTT*7`5*GMd1KEP-AY&YGa*V{g@ zfW83x3h`F`D1YRvwFearM5!F^=D}Qt`ax19{p3`QOWp#i>#80bzx;OxTcfq5PYN z8OA2At*~pQwE2fX#F+L-!a9DfH}*N=5y}dlBGxI@Ew%Zwdt8QiKB?YUYQUbp7~wI9 z56IpxH?VG4i;o8~*j@E<2<^aGuj2&Mw8OXk_E|iy^>X1Fw{0}9X9jNb{XP<51K$Pq z3x)Of4txJMA+2rNz83cZ>XfxFkUydw7>T|>c)VnFep%T3jJNB=ZZ4fy@SN~}66eHUbvxSD>s;l37tgilcAT%7 zJmY*yfigtcpF5*{c*0(FOT4?Klu0scoZr#rcWT_uS^jLZoE(GrKu2HTeSqT^`?sds z9MjKw_`tuQJla4QB-ulD_&Kjr7W}36FZl9RMpF6G^Y?uDN9?#QXPZweJpG`twm~_i z3mB`SA1Ermx8wZHSexI?^3=oj1I`a*3&jPrMK60tZ6w=#5pm!|ZT=|ziu)4G)2F*} zPre@Wfg_X=M4OYJ_(XZ@T-bfozf(!((7uZ2RR7QW@_V&0vc(Va?7W{h#M~V5%QoL2 z&XK>#4%|8ZnE4~mfAD(WrOg-bcnZF~`hq+@Ala!4bee^3{2S!$u}7SDQl7T-HesEh zZRSC=IdK*S!aSM^y9;=Z?`5g~;<1S5-vOoZkdM6}9uCWVI#O@S=gVM!MZS_hs#DTA zZ6G;1+WZa1c{%M(S@@29*Ug2*+X2ee%kEJbrNd9-9qL$S`FRe$*Kl=0_MxrGLpn$K z>0I)0eWS`UK&ia>+@r62flVcZAEQ#O_?NvU*rmgRwLIay`mJKmP6m$b3$oy1SqgxNoA zJ$$eE$tT_TDvd9H4<#9q{w9d!Ne7i3Pi6lr4bC&c&uBNKY3y3}`7B8O{RNzphsurf zQ$87cwy}NI`<6id#NrRxygvQ-fOOzIYD>pBpKf`+<9TzdP~pNu_mXT48|@w(YK?K<^g z8xWH*>zGhF{DHSiCLAaepZD|Asux+t*OpoPU5mV1M7{ zZ0dfC*IqY_$|hMUJ2CltygR}7agIGJ>Fa_TKjC@VkTzvmvazs^r?SnL8*_-ESB{V; z#t`W|WrqfyJ-d3$`HhqLtmjR=;{2j{-TzNKcZz&LCH4dSjdibs@o{XNQ^mXA0kM4P zr1I(CzXD?U?YW%-+x%{_TAsV-h$u^3M&-YadyDoxH>Y8?7Vha*yuGg9rQ;p-!?Y!B zN?XVVJWnF=eH`~Kh96yJJae=s&-hKgyTx%-+iySb`KfJ5;>~Ni|AP4IH~4WL@w|VY zGIQ>i=}8dhIsO^+AE57nI3M^(tKOq7u|5^q-^b;O=fS@~SmE0>h|1U2` znh)|m=~0lyvjH1%KWVSS+mX*ZACUhq**1`4(P=v#a{E)T);`t3p;x)XxM%wU`hkm= zAdB}y$Ot*#i}@O?w^rS-j6WoyF$#i<=3E6Pf#9?8TR|Ap>A<(Me^-@OV+0`_PWS63i4(-pd-rYX!mo+d*~NW*uD|_r*lP^ct?ui7YmXn} zd}3AfQ3LjV45(OUpm=bL@em~Q7ro?K{cn`jF!ney7 zllnu&jr)@9#<69?jxR$zm}H-<*9waFe%1L9)|TwPi(=xvG#&gn9&=xqW0()|V4A&> z?z7Gg8t*;1SShbB+20=9vXb}=;~MvM*H7PTV-4?rAs)J2LdmFY58IAcf!0jv|3E({nC za(&;oZ)2Bw{55x)cYW`*jVk^&h5O!^wTE_eE<2}E}XynXc z+W_a=TF1(>K1kpAU(hQcu4Vqkm=bmSD-d<;Bwx+lu$N4+q~E8aeza5acwQ z{l$aSS^7%Rs5jGbB5_^^9_y#H$IrSKB#uBo9^Lydp|GNRBb!@#N6n`d#OGE*&B=p+x>vP=ku;_ z+XB|RV*uI4e=lfeqA}uQzHWZNHo)%_E`50MJ)GN}0`6Bl_P3CRwxEqaZ9$u`PF$g_ z&bqZ7x2G$|x7)knu^-nXW5E)C|A}>P&UrJ&r=HZAeI3WdJZDUFn20(n9^g4`@(OHr z)c?&LV9&Ymz1h<>4FAC2+-3VO;`ijs_4hr@{3k$ffv9tqSeVa^BT0AK;uF=$llVQW z{jIRph|K!xaQAZ#1NUf|=a(wNpDFdi{4o4_iNBGa`DM_TK;Hsg2mKJl-wKU0QAd6s zfZuZYOVB0Idwrc}%-q6$Ycch6jZssN?79E8ThtG%o*yzBSTj6=eWXXQCN_O9bmx9@ z{?4E2IFfX`7rz^y;Qr4?79OC_wwX+^unFB^3`bm)=ueCH2>em zyb{e{H|CjW{u{=ej^`MZ~mi3)IM&6ubFw|0z)3IO&U0)Tyo0N*gB zou~jWnetjxfG5o4<%j@aKZ{G-k_hbY!z)*n2m8|~@{;mk{|s2REWmSK(|tb6ca&#Y zEK_;%B2S-pr)>e;qiq3PQEdSntF{2{tSx|hvMqossV#ux)Dl1^5+)!vwv1<2=?F_%1Fo7^f(4L2*}P`Tmu{_@mFDZrAII~Rt9RyLvFFU08)gQVXSVM?j|ZPO zzj4D%BY&dVTu1(xfjpDjubZW2^K<4TbieqnDQ~}RHk-|_n+NW$na{ohdEYjlX*Pdg z9)K-Q-QFoZcCB=#*}S>4g7fX$Yo$}y%a@wXTWim}vr~SI@7#RuYO{Ig_!*XkGCzIm zC$^i-yY)*y0@`VG|FiAurqTq;E6nk@#wrl0CxEUAVNbfdJbj?qd=(1%<2kd) zmu8fIo<&`Ug8n$hDo6!gp6WH>6=mhuSY@f8yHE9+O)BW}R1bM5=<-w#dO$&!r+Uak zL6^7cHLZg7f>ke~pjD3w&KY}Ds2;UC=JL}{(^sYN>r{^lwhn74IJgem!rV~KP(3Qx zI-njEY#pbVzrCZpQ}p(l@>I_%=r1@!K|z0bL#`?B`wY0QJK#k;{&B};OTX{(H_vpY OaDR7Pb=Sx0|NjT{45)Gd literal 0 HcmV?d00001 diff --git a/frontend/scripts/white_label/resources/my_company_logo.png b/frontend/scripts/white_label/resources/my_company_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8f50872743ff4611e9e205744c1446593a671fab GIT binary patch literal 4121 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 vgQ-wMvi \ No newline at end of file diff --git a/frontend/scripts/white_label/white_label.sh b/frontend/scripts/white_label/white_label.sh new file mode 100644 index 0000000000..2d6004cf9d --- /dev/null +++ b/frontend/scripts/white_label/white_label.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +# Default values +APP_NAME="AppFlowy" +APP_IDENTIFIER="com.appflowy.appflowy" +COMPANY_NAME="AppFlowy Inc." +COPYRIGHT="Copyright © 2025 AppFlowy Inc." +ICON_PATH="" +WINDOWS_ICON_PATH="" +PLATFORMS=("windows" "linux" "macos" "ios" "android") + +show_usage() { + echo "Usage: $0 [options]" + echo "Options:" + echo " --app-name Set the application name" + echo " --app-identifier Set the application identifier" + echo " --company-name Set the company name" + echo " --copyright Set the copyright information" + echo " --icon-path Set the path to the application icon (.svg)" + echo " --windows-icon-path Set the path to the windows application icon (.ico)" + echo " --platforms Comma-separated list of platforms to white label (windows,linux,macos,ios,android)" + echo " --help Show this help message" + echo "" + echo "Example:" + echo " $0 --app-name \"MyCompany\" --app-identifier \"com.mycompany.mycompany\" \\" + echo " --company-name \"MyCompany Ltd.\" --copyright \"Copyright © 2025 MyCompany Ltd.\" \\" + echo " --platforms \"windows,linux,macos\" \\" + echo " --windows-icon-path \"./assets/icons/mycompany.ico\" \\" + echo " --icon-path \"./assets/icons/mycompany.svg\"" +} + +while [[ $# -gt 0 ]]; do + case $1 in + --app-name) + APP_NAME="$2" + shift 2 + ;; + --app-identifier) + APP_IDENTIFIER="$2" + shift 2 + ;; + --company-name) + COMPANY_NAME="$2" + shift 2 + ;; + --copyright) + COPYRIGHT="$2" + shift 2 + ;; + --icon-path) + ICON_PATH="$2" + shift 2 + ;; + --windows-icon-path) + WINDOWS_ICON_PATH="$2" + shift 2 + ;; + --platforms) + IFS=',' read -ra PLATFORMS <<< "$2" + shift 2 + ;; + --help) + show_usage + exit 0 + ;; + *) + echo "Unknown option: $1" + show_usage + exit 1 + ;; + esac +done + +if [ -z "$APP_NAME" ] || [ -z "$APP_IDENTIFIER" ] || [ -z "$COMPANY_NAME" ] || [ -z "$COPYRIGHT" ] || [ -z "$ICON_PATH" ]; then + echo "Error: All parameters are required" + show_usage + exit 1 +fi + +if [ ! -f "$ICON_PATH" ]; then + echo "Error: Icon file not found at $ICON_PATH" + exit 1 +fi + +if [ ! -f "$WINDOWS_ICON_PATH" ]; then + echo "Error: Windows icon file not found at $WINDOWS_ICON_PATH" + exit 1 +fi + +run_platform_script() { + local platform=$1 + local script_path="scripts/white_label/${platform}_white_label.sh" + + if [ ! -f "$script_path" ]; then + echo -e "\033[31mWarning: White label script not found for platform: $platform\033[0m" + return + fi + + echo -e "\033[32mRunning white label script for $platform...\033[0m" + bash "$script_path" \ + --app-name "$APP_NAME" \ + --app-identifier "$APP_IDENTIFIER" \ + --company-name "$COMPANY_NAME" \ + --copyright "$COPYRIGHT" \ + --icon-path "$WINDOWS_ICON_PATH" +} + +echo -e "\033[32mRunning i18n white label script...\033[0m" +bash "scripts/white_label/i18n_white_label.sh" --company-name "$COMPANY_NAME" + +echo -e "\033[32mRunning icon white label script...\033[0m" +bash "scripts/white_label/icon_white_label.sh" --icon-path "$ICON_PATH" + +for platform in "${PLATFORMS[@]}"; do + run_platform_script "$platform" +done + +echo -e "\033[32mWhite labeling process completed successfully!\033[0m" diff --git a/frontend/scripts/white_label/windows_white_label.sh b/frontend/scripts/white_label/windows_white_label.sh new file mode 100644 index 0000000000..58801424ff --- /dev/null +++ b/frontend/scripts/white_label/windows_white_label.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +APP_NAME="AppFlowy" +APP_IDENTIFIER="com.appflowy.appflowy" +COMPANY_NAME="AppFlowy Inc." +COPYRIGHT="Copyright © 2025 AppFlowy Inc." +ICON_PATH="" + +show_usage() { + echo "Usage: $0 [options]" + echo "Options:" + echo " --app-name Set the application name" + echo " --app-identifier Set the application identifier" + echo " --company-name Set the company name" + echo " --copyright Set the copyright information" + echo " --icon-path Set the path to the application icon (.ico file)" + echo " --help Show this help message" + echo "" + echo "Example:" + echo " $0 --app-name \"MyCompany\" --app-identifier \"com.mycompany.mycompany\" \\" + echo " --company-name \"MyCompany Ltd.\" --copyright \"Copyright © 2025 MyCompany Ltd.\" \\" + echo " --icon-path \"./assets/icons/company.ico\"" +} + +while [[ $# -gt 0 ]]; do + case $1 in + --app-name) + APP_NAME="$2" + shift 2 + ;; + --app-identifier) + APP_IDENTIFIER="$2" + shift 2 + ;; + --company-name) + COMPANY_NAME="$2" + shift 2 + ;; + --copyright) + COPYRIGHT="$2" + shift 2 + ;; + --icon-path) + ICON_PATH="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --help) + show_usage + exit 0 + ;; + *) + echo "Unknown option: $1" + show_usage + exit 1 + ;; + esac +done + +if [ -z "$APP_NAME" ]; then + echo -e "\033[31mError: Application name is required\033[0m" + exit 1 +fi + +if [ -z "$APP_IDENTIFIER" ]; then + echo -e "\033[31mError: Application identifier is required\033[0m" + exit 1 +fi + +if [ -z "$COMPANY_NAME" ]; then + echo -e "\033[31mError: Company name is required\033[0m" + exit 1 +fi + +if [ -z "$COPYRIGHT" ]; then + echo -e "\033[31mError: Copyright information is required\033[0m" + exit 1 +fi + +if [ -z "$ICON_PATH" ]; then + echo -e "\033[31mError: Icon path is required\033[0m" + exit 1 +fi + +echo "Starting Windows application customization..." + +if sed --version >/dev/null 2>&1; then + SED_INPLACE="-i" +else + SED_INPLACE="-i ''" +fi + +update_runner_files() { + runner_dir="appflowy_flutter/windows/runner" + + if [ -f "$runner_dir/Runner.rc" ]; then + sed $SED_INPLACE "s/VALUE \"CompanyName\", .*$/VALUE \"CompanyName\", \"$COMPANY_NAME\"/" "$runner_dir/Runner.rc" + sed $SED_INPLACE "s/VALUE \"FileDescription\", .*$/VALUE \"FileDescription\", \"$APP_NAME\"/" "$runner_dir/Runner.rc" + sed $SED_INPLACE "s/VALUE \"InternalName\", .*$/VALUE \"InternalName\", \"$APP_NAME\"/" "$runner_dir/Runner.rc" + sed $SED_INPLACE "s/VALUE \"OriginalFilename\", .*$/VALUE \"OriginalFilename\", \"$APP_NAME.exe\"/" "$runner_dir/Runner.rc" + sed $SED_INPLACE "s/VALUE \"LegalCopyright\", .*$/VALUE \"LegalCopyright\", \"$COPYRIGHT\"/" "$runner_dir/Runner.rc" + sed $SED_INPLACE "s/VALUE \"ProductName\", .*$/VALUE \"ProductName\", \"$APP_NAME\"/" "$runner_dir/Runner.rc" + echo -e "Runner.rc updated successfully" + else + echo -e "\033[31mRunner.rc file not found\033[0m" + fi +} + +update_icon() { + if [ ! -z "$ICON_PATH" ] && [ -f "$ICON_PATH" ]; then + app_icon_path="appflowy_flutter/windows/runner/resources/app_icon.ico" + cp "$ICON_PATH" "$app_icon_path" + echo -e "Application icon updated successfully" + else + echo -e "\033[31mApplication icon file not found\033[0m" + fi +} + +update_cmake_lists() { + cmake_file="appflowy_flutter/windows/CMakeLists.txt" + if [ -f "$cmake_file" ]; then + sed $SED_INPLACE "s/set(BINARY_NAME .*)$/set(BINARY_NAME \"$APP_NAME\")/" "$cmake_file" + echo -e "CMake configuration updated successfully" + else + echo -e "\033[31mCMake configuration file not found\033[0m" + fi +} + +update_main_cpp() { + main_cpp_file="appflowy_flutter/windows/runner/main.cpp" + if [ -f "$main_cpp_file" ]; then + sed $SED_INPLACE "s/HANDLE hMutexInstance = CreateMutex(NULL, TRUE, L\"AppFlowyMutex\");/HANDLE hMutexInstance = CreateMutex(NULL, TRUE, L\"${APP_NAME}Mutex\");/" "$main_cpp_file" + sed $SED_INPLACE "s/HWND handle = FindWindowA(NULL, \"AppFlowy\");/HWND handle = FindWindowA(NULL, \"$APP_NAME\");/" "$main_cpp_file" + sed $SED_INPLACE "s/if (window.SendAppLinkToInstance(L\"AppFlowy\")) {/if (window.SendAppLinkToInstance(L\"$APP_NAME\")) {/" "$main_cpp_file" + sed $SED_INPLACE "s/if (!window.Create(L\"AppFlowy\", origin, size)) {/if (!window.Create(L\"$APP_NAME\", origin, size)) {/" "$main_cpp_file" + echo -e "main.cpp updated successfully" + else + echo -e "\033[31mMain.cpp file not found\033[0m" + fi +} + +echo "Applying customizations..." +update_runner_files +update_icon +update_cmake_lists +update_main_cpp + +echo "Windows application customization completed successfully!"