diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/.gitignore b/frontend/appflowy_flutter/packages/appflowy_editor/.gitignore deleted file mode 100644 index 7501b909b4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -.packages -build/ -coverage/ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/.metadata b/frontend/appflowy_flutter/packages/appflowy_editor/.metadata deleted file mode 100644 index d3da16e67e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - channel: stable - -project_type: package diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/CHANGELOG.md b/frontend/appflowy_flutter/packages/appflowy_editor/CHANGELOG.md deleted file mode 100644 index 0e12ebf994..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/CHANGELOG.md +++ /dev/null @@ -1,47 +0,0 @@ -## 0.0.9 -* Support customize the text color and text background color. -* Fix some bugs. - -## 0.0.8 -* Fix the toolbar display issue. -* Fix the copy/paste issue on Windows. -* Minor Updates. - -## 0.0.7 -* Refactor theme customizer, and support dark mode. -* Support export and import markdown. -* Refactor example project. -* Fix some bugs. - -## 0.0.6 -* Add three plugins: Code Block, LateX, and Horizontal rule. -* Support web platform. -* Support more markdown syntax conversions. - * `~ ~` to format text as strikethrough - * `_ _` to format text as italic - * \` \` to format text as code - * `[]()` to format text as link -* Fix some bugs. - -## 0.0.5 -* Support customize the hotkeys for a shortcut on different platforms. -* Support customize a theme. -* Support localizations. -* Support insert numbered lists. -* Fix some bugs. - -## 0.0.4 -* Support more shortcut events. -* Fix some bugs. -* Update the documentation. - -## 0.0.3 -* Support insert image. -* Support insert link. -* Fix some bugs. - -## 0.0.2 -Minor Updates to Documentation. - -## 0.0.1 -Initial Version of the library. diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/LICENSE b/frontend/appflowy_flutter/packages/appflowy_editor/LICENSE deleted file mode 100644 index 29ebfa545f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/LICENSE +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) 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 Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/README.md b/frontend/appflowy_flutter/packages/appflowy_editor/README.md deleted file mode 100644 index 65144cda59..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/README.md +++ /dev/null @@ -1,121 +0,0 @@ - - -

AppFlowy Editor

- -

A highly customizable rich-text editor for Flutter

- -

- Discord • - Twitter -

- -

- - - -

- -
- -
- -## Key Features - -* Build rich, intuitive editors -* Design and modify an ever expanding list of customizable features including - * components (such as form input controls, numbered lists, and rich text widgets) - * shortcut events - * themes - * menu options (**coming soon!**) -* [Test-coverage](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/documentation/testing.md) and ongoing maintenance by AppFlowy's core team and community of more than 1,000 builders - -## Getting Started - -Add the AppFlowy editor [Flutter package](https://docs.flutter.dev/development/packages-and-plugins/using-packages) to your environment. - -```shell -flutter pub add appflowy_editor -flutter pub get -``` - -## Creating Your First Editor - -Start by creating a new empty AppFlowyEditor object. - -```dart -final editorState = EditorState.empty(); // an empty state -final editor = AppFlowyEditor( - editorState: editorState, -); -``` - -You can also create an editor from a JSON object in order to configure your initial state. Or you can [create an editor from Markdown or Quill Delta](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/documentation/importing.md). - -```dart -final json = ...; -final editorState = EditorState(Document.fromJson(data)); -final editor = AppFlowyEditor( - editorState: editorState, -); -``` - -> Note: The parameters `localizationsDelegates` need to be assigned in MaterialApp widget -```dart -MaterialApp( - localizationsDelegates: const [ - AppFlowyEditorLocalizations.delegate, - ], -); -``` - -To get a sense of how the AppFlowy Editor works, run our example: - -```shell -git clone https://github.com/AppFlowy-IO/AppFlowy.git -cd frontend/appflowy_flutter/packages/appflowy_editor/example -flutter run -``` - -## Customizing Your Editor - -### Customizing Components - -Please refer to our documentation on customizing AppFlowy for a detailed discussion about [customizing components](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/documentation/customizing.md#customize-a-component). - -Below are some examples of component customizations: - - * [Checkbox Text](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart) demonstrates how to extend new styles based on existing rich text components - * [Image](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/network_image_node_widget.dart) demonstrates how to extend a new node and render it - * See further examples of [rich-text plugins](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text) - -### Customizing Shortcut Events - -Please refer to our documentation on customizing AppFlowy for a detailed discussion about [customizing shortcut events](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/documentation/customizing.md#customize-a-shortcut-event). - -Below are some examples of shortcut event customizations: - - * [BIUS](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart) demonstrates how to make text bold/italic/underline/strikethrough through shortcut keys - * [Paste HTML](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart) gives you an idea on how to handle pasted styles through shortcut keys - * Need more examples? Check out [Internal key event handlers](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers) - -## Glossary -Please refer to the API documentation. - -## Contributing -Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. - -Please look at [CONTRIBUTING.md](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/contributing-to-appflowy) for details. - -## License -Distributed under the AGPLv3 License. See [LICENSE](https://github.com/AppFlowy-IO/AppFlowy-Docs/blob/main/LICENSE) for more information. diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/analysis_options.yaml b/frontend/appflowy_flutter/packages/appflowy_editor/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/check.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/check.svg deleted file mode 100644 index 8446cced9f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/check.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/checkmark.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/checkmark.svg deleted file mode 100644 index f9c848f713..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/checkmark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/clear.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/clear.svg deleted file mode 100644 index 7f303d737f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/clear.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/copy.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/copy.svg deleted file mode 100644 index 101cf34205..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/copy.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/delete.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/delete.svg deleted file mode 100644 index b7f242542d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/delete.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_center.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_center.svg deleted file mode 100644 index ae9c2cfd44..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_center.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_left.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_left.svg deleted file mode 100644 index b4f2d0101e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_left.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_right.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_right.svg deleted file mode 100644 index 86a1facaac..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/align_right.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/copy.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/copy.svg deleted file mode 100644 index 101cf34205..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/copy.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/delete.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/delete.svg deleted file mode 100644 index 5a3d972872..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/delete.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/divider.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/divider.svg deleted file mode 100644 index 3e57a6b000..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/divider.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/share.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/share.svg deleted file mode 100644 index 279e7ac471..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/image_toolbar/share.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/link.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/link.svg deleted file mode 100644 index 5fbcc8d787..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/link.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/point.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/point.svg deleted file mode 100644 index be88518d0d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/point.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/quote.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/quote.svg deleted file mode 100644 index 1393e71556..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/quote.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/bulleted_list.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/bulleted_list.svg deleted file mode 100644 index 97a2e9c434..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/bulleted_list.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/checkbox.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/checkbox.svg deleted file mode 100644 index 37f52c47ed..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/checkbox.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h1.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h1.svg deleted file mode 100644 index 6e97796956..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h1.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h2.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h2.svg deleted file mode 100644 index 2c1d1d9d1c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h2.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h3.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h3.svg deleted file mode 100644 index 8c6276263d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/h3.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/image.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/image.svg deleted file mode 100644 index 0e2aafe0ec..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/image.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/number.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/number.svg deleted file mode 100644 index 9d8b98d10d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/number.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/quote.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/quote.svg deleted file mode 100644 index 5c1cbb4a50..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/quote.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/text.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/text.svg deleted file mode 100644 index 7befa5080f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/selection_menu/text.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/bold.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/bold.svg deleted file mode 100644 index 85640695af..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/bold.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/bulleted_list.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/bulleted_list.svg deleted file mode 100644 index c2c962fa0b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/bulleted_list.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/center.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/center.svg deleted file mode 100644 index ea834a35bd..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/center.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/code.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/code.svg deleted file mode 100644 index 9b96b3c2dc..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/code.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/divider.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/divider.svg deleted file mode 100644 index 3e57a6b000..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/divider.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h1.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h1.svg deleted file mode 100644 index 8a87ece4f0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h1.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h2.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h2.svg deleted file mode 100644 index 9ce394b0b1..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h2.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h3.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h3.svg deleted file mode 100644 index 43af128937..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/h3.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/highlight.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/highlight.svg deleted file mode 100644 index 697603a054..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/highlight.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/italic.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/italic.svg deleted file mode 100644 index 6b739a761f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/italic.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/left.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/left.svg deleted file mode 100644 index b4f2d0101e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/left.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/link.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/link.svg deleted file mode 100644 index 279e7ac471..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/link.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/number_list.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/number_list.svg deleted file mode 100644 index 2db0ab3b64..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/number_list.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/quote.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/quote.svg deleted file mode 100644 index 8e55d9e2e3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/quote.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/right.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/right.svg deleted file mode 100644 index 86a1facaac..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/right.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/strikethrough.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/strikethrough.svg deleted file mode 100644 index b37bb9acc0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/strikethrough.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/underline.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/underline.svg deleted file mode 100644 index 933471e6a7..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/toolbar/underline.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/uncheck.svg b/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/uncheck.svg deleted file mode 100644 index 6c487795c6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/assets/images/uncheck.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/customizing.md b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/customizing.md deleted file mode 100644 index 89a6eecf9c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/customizing.md +++ /dev/null @@ -1,393 +0,0 @@ -# Customizing Editor Features - -## Customizing a Shortcut Event - -We will use a simple example to illustrate how to quickly add a shortcut event. - -In this example, text that starts and ends with an underscore ( \_ ) character will be rendered in italics for emphasis. So typing `_xxx_` will automatically be converted into _xxx_. - -Let's start with a blank document: - -```dart -@override -Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: Alignment.topCenter, - child: AppFlowyEditor( - editorState: EditorState.empty(), - shortcutEvents: const [], - customBuilders: const {}, - ), - ), - ); -} -``` - -At this point, nothing magic will happen after typing `_xxx_`. - -![Before](./images/customize_a_shortcut_event_before.gif) - -To implement our shortcut event we will create a `ShortcutEvent` instance to handle an underscore input. - -We need to define `key` and `command` in a ShortCutEvent object to customize hotkeys. We recommend using the description of your event as a key. For example, if the underscore `_` is defined to make text italic, the key can be 'Underscore to italic'. - -> The command, made up of a single keyword such as `underscore` or a combination of keywords using the `+` sign in between to concatenate, is a condition that triggers a user-defined function. To see which keywords are available to define a command, please refer to [key_mapping.dart](../lib/src/service/shortcut_event/key_mapping.dart). -> If more than one commands trigger the same handler, then we use ',' to split them. For example, using CTRL and A or CMD and A to 'select all', we describe it as `cmd+a,ctrl+a`(case-insensitive). - -```dart -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; - -ShortcutEvent underscoreToItalicEvent = ShortcutEvent( - key: 'Underscore to italic', - command: 'shift+underscore', - handler: _underscoreToItalicHandler, -); - -ShortcutEventHandler _underscoreToItalicHandler = (editorState, event) { - -}; -``` - -Then, we need to determine if the currently selected node is a `TextNode` and if the selection is collapsed. - -If so, we will continue. - -```dart -// ... -ShortcutEventHandler _underscoreToItalicHandler = (editorState, event) { - // Obtain the selection and selected nodes of the current document through the 'selectionService' - // to determine whether the selection is collapsed and whether the selected node is a text node. - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final textNodes = selectionService.currentSelectedNodes.whereType(); - if (selection == null || !selection.isSingle || textNodes.length != 1) { - return KeyEventResult.ignored; - } -``` - -Now, we deal with handling the underscore. - -Look for the position of the previous underscore and -1. if one is _not_ found, return without doing anything. -2. if one is found, the text enclosed within the two underscores will be formatted to display in italics. - -```dart -// ... -ShortcutEventHandler _underscoreToItalicHandler = (editorState, event) { - // ... - - final textNode = textNodes.first; - final text = textNode.toRawString(); - // Determine if an 'underscore' already exists in the text node and only once. - final firstUnderscore = text.indexOf('_'); - final lastUnderscore = text.lastIndexOf('_'); - if (firstUnderscore == -1 || - firstUnderscore != lastUnderscore || - firstUnderscore == selection.start.offset - 1) { - return KeyEventResult.ignored; - } - - // Delete the previous 'underscore', - // update the style of the text surrounded by the two underscores to 'italic', - // and update the cursor position. - final transaction = editorState.transaction - ..deleteText(textNode, firstUnderscore, 1) - ..formatText( - textNode, - firstUnderscore, - selection.end.offset - firstUnderscore - 1, - { - BuiltInAttributeKey.italic: true, - }, - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: selection.end.offset - 1, - ), - ); - editorState.apply(transaction); - - return KeyEventResult.handled; -}; -``` - -Now our 'underscore handler' function is done and the only task left is to inject it into the AppFlowyEditor. - -```dart -@override -Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: Alignment.topCenter, - child: AppFlowyEditor( - editorState: EditorState.empty(), - customBuilders: const {}, - shortcutEvents: [ - underscoreToItalic, - ], - ), - ), - ); -} -``` - -![After](./images/customize_a_shortcut_event_after.gif) - -Check out the [complete code](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart) file of this example. - - -## Customizing a Component -We will use a simple example to show how to quickly add a custom component. - -In this example we will render an image from the network. - -Let's start with a blank document: - -```dart -@override -Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: Alignment.topCenter, - child: AppFlowyEditor( - editorState: EditorState.empty(), - shortcutEvents: const [], - customBuilders: const {}, - ), - ), - ); -} -``` - -Next, we will choose a unique string for your custom node's type. - -We'll use `network_image` in this case. And we add `network_image_src` to the `attributes` to describe the link of the image. - -```JSON -{ - "type": "network_image", - "attributes": { - "network_image_src": "https://docs.flutter.dev/assets/images/dash/dash-fainting.gif" - } -} -``` - -Then, we create a class that inherits [NodeWidgetBuilder](../lib/src/service/render_plugin_service.dart). As shown in the autoprompt, we need to implement two functions: -1. one returns a widget -2. the other verifies the correctness of the [Node](../lib/src/core/document/node.dart). - - -```dart -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; - -class NetworkImageNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - throw UnimplementedError(); - } - - @override - NodeValidator get nodeValidator => throw UnimplementedError(); -} -``` - -Now, let's implement a simple image widget based on `Image`. - -Note that the `State` object that is returned by the `Widget` must implement [Selectable](../lib/src/render/selection/selectable.dart) using the `with` keyword. - -```dart -class _NetworkImageNodeWidget extends StatefulWidget { - const _NetworkImageNodeWidget({ - Key? key, - required this.node, - }) : super(key: key); - - final Node node; - - @override - State<_NetworkImageNodeWidget> createState() => - __NetworkImageNodeWidgetState(); -} - -class __NetworkImageNodeWidgetState extends State<_NetworkImageNodeWidget> - with Selectable { - RenderBox get _renderBox => context.findRenderObject() as RenderBox; - - @override - Widget build(BuildContext context) { - return Image.network( - widget.node.attributes['network_image_src'], - height: 200, - loadingBuilder: (context, child, loadingProgress) => - loadingProgress == null ? child : const CircularProgressIndicator(), - ); - } - - @override - Position start() => Position(path: widget.node.path, offset: 0); - - @override - Position end() => Position(path: widget.node.path, offset: 1); - - @override - Position getPositionInOffset(Offset start) => end(); - - @override - List getRectsInSelection(Selection selection) => - [Offset.zero & _renderBox.size]; - - @override - Selection getSelectionInRange(Offset start, Offset end) => Selection.single( - path: widget.node.path, - startOffset: 0, - endOffset: 1, - ); - - @override - Offset localToGlobal(Offset offset) => _renderBox.localToGlobal(offset); -} -``` - -Finally, we return `_NetworkImageNodeWidget` in the `build` function of `NetworkImageNodeWidgetBuilder`... - -```dart -class NetworkImageNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return _NetworkImageNodeWidget( - key: context.node.key, - node: context.node, - ); - } - - @override - NodeValidator get nodeValidator => (node) { - return node.type == 'network_image' && - node.attributes['network_image_src'] is String; - }; -} -``` - -... and register `NetworkImageNodeWidgetBuilder` in the `AppFlowyEditor`. - -```dart -final editorState = EditorState( - document: StateTree.empty() - ..insert( - [0], - [ - TextNode.empty(), - Node.fromJson({ - 'type': 'network_image', - 'attributes': { - 'network_image_src': - 'https://docs.flutter.dev/assets/images/dash/dash-fainting.gif' - } - }) - ], - ), -); -return AppFlowyEditor( - editorState: editorState, - shortcutEvents: const [], - customBuilders: { - 'network_image': NetworkImageNodeWidgetBuilder(), - }, -); -``` - -![Whew!](./images/customize_a_component.gif) - -Check out the [complete code](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/network_image_node_widget.dart) file of this example. - -## Customizing a Theme (New Feature in 0.0.7) - -We will use a simple example to illustrate how to quickly customize a theme. - -Let's start with a blank document: - -```dart -@override -Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: Alignment.topCenter, - child: AppFlowyEditor( - editorState: EditorState.empty(), - shortcutEvents: const [], - customBuilders: const {}, - ), - ), - ); -} -``` - -At this point, the editor looks like ... -![Before](./images/customizing_a_theme_before.png) - - -Next, we will customize the `EditorStyle`. - -```dart -ThemeData customizeEditorTheme(BuildContext context) { - final dark = EditorStyle.dark; - final editorStyle = dark.copyWith( - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 150), - cursorColor: Colors.red.shade600, - selectionColor: Colors.yellow.shade600.withOpacity(0.5), - textStyle: GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.white, - ), - placeholderTextStyle: GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.grey.shade400, - ), - code: dark.code?.copyWith( - backgroundColor: Colors.lightBlue.shade200, - fontStyle: FontStyle.italic, - ), - highlightColorHex: '0x60FF0000', // red - ); - - final quote = QuotedTextPluginStyle.dark.copyWith( - textStyle: (_, __) => GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.blue.shade400, - fontStyle: FontStyle.italic, - fontWeight: FontWeight.w700, - ), - ); - - return Theme.of(context).copyWith(extensions: [ - editorStyle, - ...darkPluginStyleExtension, - quote, - ]); -} -``` - -Now our 'customize style' function is done and the only task left is to inject it into the AppFlowyEditor. - -```dart -@override -Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: Alignment.topCenter, - child: AppFlowyEditor( - editorState: EditorState.empty(), - themeData: customizeEditorTheme(context), - shortcutEvents: const [], - customBuilders: const {}, - ), - ), - ); -} -``` - -![After](./images/customizing_a_theme_after.png) \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/appflowy_editor_example.mp4 b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/appflowy_editor_example.mp4 deleted file mode 100644 index 3b1b97f847..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/appflowy_editor_example.mp4 and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_component.gif b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_component.gif deleted file mode 100644 index e78f0b2f04..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_component.gif and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_shortcut_event_after.gif b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_shortcut_event_after.gif deleted file mode 100644 index e93035c705..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_shortcut_event_after.gif and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_shortcut_event_before.gif b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_shortcut_event_before.gif deleted file mode 100644 index 89d31b8d13..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customize_a_shortcut_event_before.gif and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customizing_a_theme_after.png b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customizing_a_theme_after.png deleted file mode 100644 index 3890829222..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customizing_a_theme_after.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customizing_a_theme_before.png b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customizing_a_theme_before.png deleted file mode 100644 index 8eaac244a5..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/customizing_a_theme_before.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/example.png b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/example.png deleted file mode 100644 index 9bd16610dc..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/example.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/reporting_bugs.png b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/reporting_bugs.png deleted file mode 100644 index 5d4d7f6150..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/images/reporting_bugs.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/importing.md b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/importing.md deleted file mode 100644 index 393f4dc62e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/importing.md +++ /dev/null @@ -1,36 +0,0 @@ -# Importing data - -For now, we have supported three ways to import data to initialize AppFlowy Editor. - -1. From AppFlowy Document JSON - -```dart -const document = r'''{"document":{"type":"editor","children":[{"type":"text","attributes":{"subtype":"heading","heading":"h1"},"delta":[{"insert":"Hello AppFlowy!"}]}]}}'''; -final json = jsonDecode(document); -final editorState = EditorState( - document: Document.fromJson( - Map.from(json), - ), -); -``` - -2. From Markdown - -```dart -const markdown = r'''# Hello AppFlowy!'''; -final editorState = EditorState( - document: markdownToDocument(markdown), -); -``` - -3. From Quill Delta - -```dart -const delta = r'''[{"insert":"Hello AppFlowy!"},{"attributes":{"header":1},"insert":"\n"}]'''; -final json = jsonDecode(delta); -final editorState = EditorState( - document: DeltaDocumentConvert().convertFromJSON(json), -); -``` - -For more details, please refer to the function `_importFile` through this [link](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/home_page.dart). \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/testing.md b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/testing.md deleted file mode 100644 index b388edddad..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/testing.md +++ /dev/null @@ -1,145 +0,0 @@ -# Testing - -The directory structure of test files mirrors that of the code files, making it easy for us to map a file with the corresponding test and check if the test is updated. - -For an overview of testing best practices in Flutter applications, please refer to Flutter's [introduction to widget testing](https://docs.flutter.dev/cookbook/testing/widget/introduction) as well as their [introduction to unit testing](https://docs.flutter.dev/cookbook/testing/unit/introduction). -There you will learn how to do such things as such as simulate a click as well as leverage the `test` and `expect` functions. - -## Testing Basic Editor Functions - -The example code below shows how to construct a document that will be used in our testing. - -```dart -const text = 'Welcome to Appflowy 😁'; -// Get the instance of the editor. -final editor = tester.editor; - -// Insert an empty text node. -editor.insertEmptyTextNode(); - -// Insert a text node with the text string we defined earlier. -editor.insertTextNode(text); - -// Insert the same text, but with the heading style. -editor.insertTextNode(text, attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h1, -}); - -// Insert our text with the bulleted list style and the bold style. -// If you want to modify the style of the inserted text, you need to use the Delta parameter. -editor.insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }, - delta: Delta([ - TextInsert(text, {BuiltInAttributeKey.bold: true}), - ]), -); -``` - -The `startTesting` function of the editor must be called before you begin your test. - -```dart -await editor.startTesting(); -``` - -Get the number of nodes in the document. - -```dart -final length = editor.documentLength; -print(length); -``` - -Get the node of a defined path. In this case we are getting the first node of the document which is the text "Welcome to Appflowy 😁". - -```dart -final firstTextNode = editor.nodeAtPath([0]) as TextNode; -``` - -Update the [Selection](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/document/selection.dart) so that our text "Welcome to Appflowy 😁" is selected. We will start our selection from the beginning of the string. - -```dart -await editor.updateSelection( - Selection.single(path: firstTextNode.path, startOffset: 0), -); -``` - -Get the current selection. - -```dart -final selection = editor.documentSelection; -print(selection); -``` - -Next we will simulate the input of a shortcut key being pressed that will select all the text. - -```dart -// Meta + A. -await editor.pressLogicKey(key: LogicalKeyboardKey.keyA, isMetaPressed: true); -// Meta + shift + S. -await editor.pressLogicKey - key: LogicalKeyboardKey.keyS, - isMetaPressed: true, - isShiftPressed: true, -); -``` - -We will then simulate text input. - -```dart -// Insert 'Hello World' at the beginning of the first node. -editor.insertText(firstTextNode, 'Hello World', 0); -``` - -Once the text has been added, we can get information about the text node. - -```dart -// Get the text of the first text node as plain text -final textAfterInserted = firstTextNode.toRawString(); -print(textAfterInserted); -// Get the attributes of the text node -final attributes = firstTextNode.attributes; -print(attributes); -``` - -## A Complete Code Example - -In the example code below we are going to test `select_all_handler.dart` by inserting 100 lines of text that read "Welcome to Appflowy 😁" and then simulating the "selectAll" shortcut key being pressed. - -Afterwards, we will `expect` that the current selection of the editor is equal to the selection of all the lines that were generated. - -```dart -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('select_all_handler_test.dart', () { - testWidgets('Presses Command + A in the document', (tester) async { - const lines = 100; - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor; - for (var i = 0; i < lines; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyA, isMetaPressed: true); - - expect( - editor.documentSelection, - Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [lines - 1], offset: text.length), - ), - ); - }); - }); -} -``` diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/translation.md b/frontend/appflowy_flutter/packages/appflowy_editor/documentation/translation.md deleted file mode 100644 index 8fc26d8012..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/documentation/translation.md +++ /dev/null @@ -1,16 +0,0 @@ -# Translate AppFlowy Editor - -You can help Appflowy Editor in supporting various languages by contributing. Follow the steps below sequentially to contribute translations. - -## Steps to modify an existing translation -Translation files are located in: `frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/` -1. Install the Visual Studio Code plugin: [Flutter intl](https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl) -2. Modify the specific translation file. -3. Save the file and the translation will be generated automatically. - -## Steps to add new language -Translation files are located in: `frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/` -1. Install the Visual Studio Code plugin: [Flutter intl](https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl) -2. Copy the `intl_en.arb` as a base translation and rename the new file to `intl_.arb` -3. Modify the new translation file. -4. Save the file and the translation will be generated automatically. \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/.firebaserc b/frontend/appflowy_flutter/packages/appflowy_editor/example/.firebaserc deleted file mode 100644 index 06fcc074c4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/.firebaserc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "projects": { - "default": "appflowy-editor" - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/.gitignore b/frontend/appflowy_flutter/packages/appflowy_editor/example/.gitignore deleted file mode 100644 index a8e938c083..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/.gitignore +++ /dev/null @@ -1,47 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/.metadata b/frontend/appflowy_flutter/packages/appflowy_editor/example/.metadata deleted file mode 100644 index ed0b5185fb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/.metadata +++ /dev/null @@ -1,45 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled. - -version: - revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - channel: stable - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - - platform: android - create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - - platform: ios - create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - - platform: linux - create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - - platform: macos - create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - - platform: web - create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - - platform: windows - create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/README.md b/frontend/appflowy_flutter/packages/appflowy_editor/example/README.md deleted file mode 100644 index 2b3fce4c86..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# example - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/analysis_options.yaml b/frontend/appflowy_flutter/packages/appflowy_editor/example/analysis_options.yaml deleted file mode 100644 index 61b6c4de17..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/analysis_options.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/.gitignore b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/.gitignore deleted file mode 100644 index 6f568019d3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties -**/*.keystore -**/*.jks diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/build.gradle b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/build.gradle deleted file mode 100644 index 0833ecfca8..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/build.gradle +++ /dev/null @@ -1,71 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/debug/AndroidManifest.xml b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 45d523a2a2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/AndroidManifest.xml b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 3f41384dbc..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt deleted file mode 100644 index e793a000d6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/drawable-v21/launch_background.xml b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f3f6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/drawable/launch_background.xml b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f884..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 09d4391482..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebd..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/values-night/styles.xml b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 06952be745..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/values/styles.xml b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index cb1ef88056..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/profile/AndroidManifest.xml b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 45d523a2a2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/build.gradle b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/build.gradle deleted file mode 100644 index 83ae220041..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.6.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/gradle.properties b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/gradle.properties deleted file mode 100644 index 94adc3a3f9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/gradle/wrapper/gradle-wrapper.properties b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index cc5527d781..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/settings.gradle b/frontend/appflowy_flutter/packages/appflowy_editor/example/android/settings.gradle deleted file mode 100644 index 44e62bcf06..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/assets/example.json b/frontend/appflowy_flutter/packages/appflowy_editor/example/assets/example.json deleted file mode 100644 index 74c82e3227..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/assets/example.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "document": { - "type": "editor", - "children": [ - { - "type": "text", - "attributes": { - "subtype": "heading", - "heading": "h2" - }, - "delta": [ - { "insert": "👋 " }, - { "insert": "Welcome to", "attributes": { "bold": true } }, - { "insert": " " }, - { - "insert": "AppFlowy Editor", - "attributes": { - "href": "appflowy.io", - "italic": true, - "bold": true - } - } - ] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "delta": [ - { "insert": "AppFlowy Editor is a" }, - { "insert": " " }, - { "insert": "highly customizable", "attributes": { "bold": true } }, - { "insert": " " }, - { "insert": "rich-text editor", "attributes": { "italic": true } }, - { "insert": " for " }, - { "insert": "Flutter", "attributes": { "underline": true } } - ] - }, - { - "type": "text", - "attributes": { "checkbox": true, "subtype": "checkbox" }, - "delta": [{ "insert": "Customizable" }] - }, - { - "type": "text", - "attributes": { "checkbox": true, "subtype": "checkbox" }, - "delta": [{ "insert": "Test-covered" }] - }, - { - "type": "text", - "attributes": { "checkbox": false, "subtype": "checkbox" }, - "delta": [{ "insert": "more to come!" }] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "attributes": { "subtype": "quote" }, - "delta": [{ "insert": "Here is an example you can give a try" }] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "delta": [ - { "insert": "You can also use " }, - { - "insert": "AppFlowy Editor", - "attributes": { - "italic": true, - "bold": true, - "backgroundColor": "0x6000BCF0" - } - }, - { "insert": " as a component to build your own app." } - ] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "attributes": { "subtype": "bulleted-list" }, - "delta": [{ "insert": "Use / to insert blocks" }] - }, - { - "type": "text", - "attributes": { "subtype": "bulleted-list" }, - "delta": [ - { - "insert": "Select text to trigger to the toolbar to format your notes." - } - ] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "delta": [ - { - "insert": "If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!" - } - ] - } - ] - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/assets/images/icon.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/assets/images/icon.png deleted file mode 100644 index 95e504908b..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/assets/images/icon.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/firebase.json b/frontend/appflowy_flutter/packages/appflowy_editor/example/firebase.json deleted file mode 100644 index bba899ee87..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/firebase.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "hosting": { - "public": "build/web", - "ignore": [ - "firebase.json", - "**/.*", - "**/node_modules/**" - ], - "rewrites": [ - { - "source": "**", - "destination": "/index.html" - } - ], - "headers": [ { - "source": "**/*.@(png|jpg|jpeg|gif)", - "headers": [ { - "key": "Access-Control-Allow-Origin", - "value": "*" - } ] - } ] - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/.gitignore b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/.gitignore deleted file mode 100644 index 7a7f9873ad..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/AppFrameworkInfo.plist b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 8d4492f977..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 9.0 - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/Debug.xcconfig b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index ec97fc6f30..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/Release.xcconfig b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index c4855bfe20..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Podfile b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Podfile deleted file mode 100644 index 1e8c3c90a5..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.pbxproj b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 813642b9a4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,484 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 446D3AAR7E; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 446D3AAR7E; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 446D3AAR7E; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a625..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index c87d15a335..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16ed..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/AppDelegate.swift b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4a8c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf0301..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 2ccbfd967d..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bc..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cde12118d..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7ed..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index dcdc2306c2..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 2ccbfd967d..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5c..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609d..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609d..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39d..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 6a84f41e14..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index d0e1f58536..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2fd4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b70..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7c9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Base.lproj/Main.storyboard b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Info.plist b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Info.plist deleted file mode 100644 index 907f329fe0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Example - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Runner-Bridging-Header.h b/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a560b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/expandable_floating_action_button.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/expandable_floating_action_button.dart deleted file mode 100644 index 01da3ab593..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/expandable_floating_action_button.dart +++ /dev/null @@ -1,234 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter/material.dart'; - -// copy from https://docs.flutter.dev/cookbook/effects/expandable-fab -@immutable -class ExpandableFab extends StatefulWidget { - const ExpandableFab({ - super.key, - this.initialOpen, - required this.distance, - required this.children, - }); - - final bool? initialOpen; - final double distance; - final List children; - - @override - State createState() => _ExpandableFabState(); -} - -class _ExpandableFabState extends State - with SingleTickerProviderStateMixin { - late final AnimationController _controller; - late final Animation _expandAnimation; - bool _open = false; - - @override - void initState() { - super.initState(); - _open = widget.initialOpen ?? false; - _controller = AnimationController( - value: _open ? 1.0 : 0.0, - duration: const Duration(milliseconds: 250), - vsync: this, - ); - _expandAnimation = CurvedAnimation( - curve: Curves.fastOutSlowIn, - reverseCurve: Curves.easeOutQuad, - parent: _controller, - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _toggle() { - setState(() { - _open = !_open; - if (_open) { - _controller.forward(); - } else { - _controller.reverse(); - } - }); - } - - @override - Widget build(BuildContext context) { - return SizedBox.expand( - child: Stack( - alignment: Alignment.bottomRight, - clipBehavior: Clip.none, - children: [ - _buildTapToCloseFab(), - ..._buildExpandingActionButtons(), - _buildTapToOpenFab(), - ], - ), - ); - } - - Widget _buildTapToCloseFab() { - return SizedBox( - width: 56.0, - height: 56.0, - child: Center( - child: Material( - shape: const CircleBorder(), - clipBehavior: Clip.antiAlias, - elevation: 4.0, - child: InkWell( - onTap: _toggle, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon( - Icons.close, - color: Theme.of(context).primaryColor, - ), - ), - ), - ), - ), - ); - } - - List _buildExpandingActionButtons() { - final children = []; - final count = widget.children.length; - final step = 90.0 / (count - 1); - for (var i = 0, angleInDegrees = 0.0; - i < count; - i++, angleInDegrees += step) { - children.add( - _ExpandingActionButton( - directionInDegrees: angleInDegrees, - maxDistance: widget.distance, - progress: _expandAnimation, - child: widget.children[i], - ), - ); - } - return children; - } - - Widget _buildTapToOpenFab() { - return IgnorePointer( - ignoring: _open, - child: AnimatedContainer( - transformAlignment: Alignment.center, - transform: Matrix4.diagonal3Values( - _open ? 0.7 : 1.0, - _open ? 0.7 : 1.0, - 1.0, - ), - duration: const Duration(milliseconds: 250), - curve: const Interval(0.0, 0.5, curve: Curves.easeOut), - child: AnimatedOpacity( - opacity: _open ? 0.0 : 1.0, - curve: const Interval(0.25, 1.0, curve: Curves.easeInOut), - duration: const Duration(milliseconds: 250), - child: FloatingActionButton( - onPressed: _toggle, - child: const Icon(Icons.create), - ), - ), - ), - ); - } -} - -@immutable -class _ExpandingActionButton extends StatelessWidget { - const _ExpandingActionButton({ - required this.directionInDegrees, - required this.maxDistance, - required this.progress, - required this.child, - }); - - final double directionInDegrees; - final double maxDistance; - final Animation progress; - final Widget child; - - @override - Widget build(BuildContext context) { - return AnimatedBuilder( - animation: progress, - builder: (context, child) { - final offset = Offset.fromDirection( - directionInDegrees * (math.pi / 180.0), - progress.value * maxDistance, - ); - return Positioned( - right: 4.0 + offset.dx, - bottom: 4.0 + offset.dy, - child: Transform.rotate( - angle: (1.0 - progress.value) * math.pi / 2, - child: child!, - ), - ); - }, - child: FadeTransition( - opacity: progress, - child: child, - ), - ); - } -} - -@immutable -class ActionButton extends StatelessWidget { - const ActionButton({ - super.key, - this.onPressed, - required this.icon, - }); - - final VoidCallback? onPressed; - final Widget icon; - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - return Material( - shape: const CircleBorder(), - clipBehavior: Clip.antiAlias, - color: theme.colorScheme.secondary, - elevation: 4.0, - child: IconButton( - onPressed: onPressed, - icon: icon, - color: theme.colorScheme.onSecondary, - ), - ); - } -} - -@immutable -class FakeItem extends StatelessWidget { - const FakeItem({ - super.key, - required this.isBig, - }); - - final bool isBig; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0), - height: isBig ? 128.0 : 36.0, - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(8.0)), - color: Colors.grey.shade300, - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/home_page.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/home_page.dart deleted file mode 100644 index 293d7a6445..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/home_page.dart +++ /dev/null @@ -1,379 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:example/pages/simple_editor.dart'; -import 'package:example/plugin/AI/text_robot.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:universal_html/html.dart' as html; - -enum ExportFileType { - json, - markdown, - html, - delta, -} - -extension on ExportFileType { - String get extension { - switch (this) { - case ExportFileType.json: - case ExportFileType.delta: - return 'json'; - case ExportFileType.markdown: - return 'md'; - case ExportFileType.html: - return 'html'; - } - } -} - -class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); - - @override - State createState() => _HomePageState(); -} - -class _HomePageState extends State { - final _scaffoldKey = GlobalKey(); - late WidgetBuilder _widgetBuilder; - late EditorState _editorState; - late Future _jsonString; - ThemeData _themeData = ThemeData.light().copyWith( - extensions: [ - ...lightEditorStyleExtension, - ...lightPluginStyleExtension, - ], - ); - - @override - void initState() { - super.initState(); - - _jsonString = rootBundle.loadString('assets/example.json'); - _widgetBuilder = (context) => SimpleEditor( - jsonString: _jsonString, - themeData: _themeData, - onEditorStateChange: (editorState) { - _editorState = editorState; - }, - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - key: _scaffoldKey, - extendBodyBehindAppBar: true, - drawer: _buildDrawer(context), - body: _buildBody(context), - floatingActionButton: _buildFloatingActionButton(context), - ); - } - - Widget _buildDrawer(BuildContext context) { - return Drawer( - child: ListView( - padding: EdgeInsets.zero, - children: [ - DrawerHeader( - padding: EdgeInsets.zero, - margin: EdgeInsets.zero, - child: Image.asset( - 'assets/images/icon.png', - fit: BoxFit.fill, - ), - ), - - // AppFlowy Editor Demo - _buildSeparator(context, 'AppFlowy Editor Demo'), - _buildListTile(context, 'With Example.json', () { - final jsonString = rootBundle.loadString('assets/example.json'); - _loadEditor(context, jsonString); - }), - _buildListTile(context, 'With Empty Document', () { - final jsonString = Future.value( - jsonEncode(EditorState.empty().document.toJson()).toString(), - ); - _loadEditor(context, jsonString); - }), - - // Text Robot - _buildSeparator(context, 'Text Robot'), - _buildListTile(context, 'Type Text Automatically', () async { - final jsonString = Future.value( - jsonEncode(EditorState.empty().document.toJson()).toString(), - ); - await _loadEditor(context, jsonString); - - Future.delayed(const Duration(seconds: 2), () { - final textRobot = TextRobot( - editorState: _editorState, - ); - textRobot.insertText( - r''' -Section 1.10.32 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC -"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?" - -1914 translation by H. Rackham -"But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?" -''', - ); - }); - }), - - // Encoder Demo - _buildSeparator(context, 'Encoder Demo'), - _buildListTile(context, 'Export To JSON', () { - _exportFile(_editorState, ExportFileType.json); - }), - _buildListTile(context, 'Export to Markdown', () { - _exportFile(_editorState, ExportFileType.markdown); - }), - - // Decoder Demo - _buildSeparator(context, 'Decoder Demo'), - _buildListTile(context, 'Import From JSON', () { - _importFile(ExportFileType.json); - }), - _buildListTile(context, 'Import From Markdown', () { - _importFile(ExportFileType.markdown); - }), - _buildListTile(context, 'Import From Quill Delta', () { - _importFile(ExportFileType.delta); - }), - - // Theme Demo - _buildSeparator(context, 'Theme Demo'), - _buildListTile(context, 'Built In Dark Mode', () { - _jsonString = Future.value( - jsonEncode(_editorState.document.toJson()).toString(), - ); - setState(() { - _themeData = ThemeData.dark().copyWith( - extensions: [ - ...darkEditorStyleExtension, - ...darkPluginStyleExtension, - ], - ); - }); - }), - _buildListTile(context, 'Custom Theme', () { - _jsonString = Future.value( - jsonEncode(_editorState.document.toJson()).toString(), - ); - setState(() { - _themeData = _customizeEditorTheme(context); - }); - }), - ], - ), - ); - } - - Widget _buildBody(BuildContext context) { - return _widgetBuilder(context); - } - - Widget _buildListTile( - BuildContext context, - String text, - VoidCallback? onTap, - ) { - return ListTile( - dense: true, - contentPadding: const EdgeInsets.only(left: 16), - title: Text( - text, - style: const TextStyle( - color: Colors.blue, - fontSize: 14, - ), - ), - onTap: () { - Navigator.pop(context); - onTap?.call(); - }, - ); - } - - Widget _buildSeparator(BuildContext context, String text) { - return Padding( - padding: const EdgeInsets.only(left: 16, top: 16, bottom: 4), - child: Text( - text, - style: const TextStyle( - color: Colors.grey, - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - ); - } - - Widget _buildFloatingActionButton(BuildContext context) { - return FloatingActionButton( - onPressed: () { - _scaffoldKey.currentState?.openDrawer(); - }, - child: const Icon(Icons.menu), - ); - } - - Future _loadEditor( - BuildContext context, - Future jsonString, - ) async { - final completer = Completer(); - _jsonString = jsonString; - setState( - () { - _widgetBuilder = (context) => SimpleEditor( - jsonString: _jsonString, - themeData: _themeData, - onEditorStateChange: (editorState) { - _editorState = editorState; - }, - ); - }, - ); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - completer.complete(); - }); - return completer.future; - } - - void _exportFile( - EditorState editorState, - ExportFileType fileType, - ) async { - var result = ''; - - switch (fileType) { - case ExportFileType.json: - result = jsonEncode(editorState.document.toJson()); - break; - case ExportFileType.markdown: - result = documentToMarkdown(editorState.document); - break; - case ExportFileType.html: - case ExportFileType.delta: - throw UnimplementedError(); - } - - if (!kIsWeb) { - final path = await FilePicker.platform.saveFile( - fileName: 'document.${fileType.extension}', - ); - if (path != null) { - await File(path).writeAsString(result); - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('This document is saved to the $path'), - ), - ); - } - } - } else { - final blob = html.Blob([result], 'text/plain', 'native'); - html.AnchorElement( - href: html.Url.createObjectUrlFromBlob(blob).toString(), - ) - ..setAttribute('download', 'document.${fileType.extension}') - ..click(); - } - } - - void _importFile(ExportFileType fileType) async { - final result = await FilePicker.platform.pickFiles( - allowMultiple: false, - allowedExtensions: [fileType.extension], - type: FileType.custom, - ); - var plainText = ''; - if (!kIsWeb) { - final path = result?.files.single.path; - if (path == null) { - return; - } - plainText = await File(path).readAsString(); - } else { - final bytes = result?.files.first.bytes; - if (bytes == null) { - return; - } - plainText = const Utf8Decoder().convert(bytes); - } - - var jsonString = ''; - switch (fileType) { - case ExportFileType.json: - jsonString = plainText; - break; - case ExportFileType.markdown: - jsonString = jsonEncode(markdownToDocument(plainText).toJson()); - break; - case ExportFileType.delta: - jsonString = jsonEncode( - DeltaDocumentConvert() - .convertFromJSON( - jsonDecode( - plainText.replaceAll('\\\\\n', '\\n'), - ), - ) - .toJson(), - ); - break; - case ExportFileType.html: - throw UnimplementedError(); - } - - if (mounted) { - _loadEditor(context, Future.value(jsonString)); - } - } - - ThemeData _customizeEditorTheme(BuildContext context) { - final dark = EditorStyle.dark; - final editorStyle = dark.copyWith( - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 150), - cursorColor: Colors.blue.shade600, - selectionColor: Colors.yellow.shade600.withOpacity(0.5), - textStyle: GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.grey, - ), - placeholderTextStyle: GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.grey.shade500, - ), - code: dark.code?.copyWith( - backgroundColor: Colors.lightBlue.shade200, - fontStyle: FontStyle.italic, - ), - highlightColorHex: '0x60FF0000', // red - ); - - final quote = QuotedTextPluginStyle.dark.copyWith( - textStyle: (_, __) => GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.blue.shade400, - fontStyle: FontStyle.italic, - fontWeight: FontWeight.w700, - ), - ); - - return Theme.of(context).copyWith(extensions: [ - editorStyle, - ...darkPluginStyleExtension, - quote, - ]); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/main.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/main.dart deleted file mode 100644 index b98cec364a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/main.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:example/home_page.dart'; -import 'package:flutter/material.dart'; - -import 'package:flutter_localizations/flutter_localizations.dart'; - -import 'package:appflowy_editor/appflowy_editor.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - AppFlowyEditorLocalizations.delegate, - ], - supportedLocales: [Locale('en', 'US')], - debugShowCheckedModeBanner: false, - home: MyHomePage(title: 'AppFlowyEditor Example'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key, required this.title}) : super(key: key); - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - @override - Widget build(BuildContext context) { - return const HomePage(); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/pages/simple_editor.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/pages/simple_editor.dart deleted file mode 100644 index 9d7aebf7ff..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/pages/simple_editor.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; -import 'package:example/plugin/AI/continue_to_write.dart'; -import 'package:example/plugin/AI/auto_completion.dart'; -import 'package:example/plugin/AI/gpt3.dart'; -import 'package:example/plugin/AI/smart_edit.dart'; -import 'package:flutter/material.dart'; - -class SimpleEditor extends StatelessWidget { - const SimpleEditor({ - super.key, - required this.jsonString, - required this.themeData, - required this.onEditorStateChange, - }); - - final Future jsonString; - final ThemeData themeData; - final void Function(EditorState editorState) onEditorStateChange; - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: jsonString, - builder: (context, snapshot) { - if (snapshot.hasData && - snapshot.connectionState == ConnectionState.done) { - final editorState = EditorState( - document: Document.fromJson( - Map.from( - json.decode(snapshot.data!), - ), - ), - ); - editorState.logConfiguration - ..handler = debugPrint - ..level = LogLevel.all; - onEditorStateChange(editorState); - - return AppFlowyEditor( - editorState: editorState, - themeData: themeData, - autoFocus: editorState.document.isEmpty, - customBuilders: { - // Divider - kDividerType: DividerWidgetBuilder(), - // Math Equation - kMathEquationType: MathEquationNodeWidgetBuidler(), - // Code Block - kCodeBlockType: CodeBlockNodeWidgetBuilder(), - }, - shortcutEvents: [ - // Divider - insertDividerEvent, - // Code Block - enterInCodeBlock, - ignoreKeysInCodeBlock, - pasteInCodeBlock, - ], - selectionMenuItems: [ - // Divider - dividerMenuItem, - // Math Equation - mathEquationMenuItem, - // Code Block - codeBlockMenuItem, - // Emoji - emojiMenuItem, - // Open AI - if (apiKey.isNotEmpty) ...[ - autoCompletionMenuItem, - continueToWriteMenuItem, - ] - ], - toolbarItems: [ - smartEditItem, - ], - ); - } else { - return const Center( - child: CircularProgressIndicator(), - ); - } - }, - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/auto_completion.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/auto_completion.dart deleted file mode 100644 index e90f8bd3d2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/auto_completion.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:example/plugin/AI/gpt3.dart'; -import 'package:example/plugin/AI/text_robot.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -SelectionMenuItem autoCompletionMenuItem = SelectionMenuItem( - name: 'Auto generate content', - icon: (editorState, onSelected) => Icon( - Icons.rocket, - size: 18.0, - color: onSelected - ? editorState.editorStyle.selectionMenuItemSelectedIconColor - : editorState.editorStyle.selectionMenuItemIconColor, - ), - keywords: ['auto generate content', 'open ai', 'gpt3', 'ai'], - handler: ((editorState, menuService, context) async { - showDialog( - context: context, - builder: (context) { - final controller = TextEditingController(text: ''); - return AlertDialog( - content: RawKeyboardListener( - focusNode: FocusNode(), - child: TextField( - autofocus: true, - controller: controller, - maxLines: null, - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: 'Please input something...', - ), - ), - onKey: (key) { - if (key is! RawKeyDownEvent) return; - if (key.logicalKey == LogicalKeyboardKey.enter) { - Navigator.of(context).pop(); - // fetch the result and insert it - final textRobot = TextRobot(editorState: editorState); - const gpt3 = GPT3APIClient(apiKey: apiKey); - gpt3.getGPT3Completion( - controller.text, - '', - onResult: (result) async { - await textRobot.insertText( - result, - inputType: TextRobotInputType.character, - ); - }, - onError: () async {}, - ); - } else if (key.logicalKey == LogicalKeyboardKey.escape) { - Navigator.of(context).pop(); - } - }, - ), - ); - }, - ); - }), -); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/continue_to_write.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/continue_to_write.dart deleted file mode 100644 index 4e0b2ec100..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/continue_to_write.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:example/plugin/AI/gpt3.dart'; -import 'package:example/plugin/AI/text_robot.dart'; -import 'package:flutter/material.dart'; - -SelectionMenuItem continueToWriteMenuItem = SelectionMenuItem( - name: 'Continue To Write', - icon: (editorState, onSelected) => Icon( - Icons.print, - size: 18.0, - color: onSelected - ? editorState.editorStyle.selectionMenuItemSelectedIconColor - : editorState.editorStyle.selectionMenuItemIconColor, - ), - keywords: ['continue to write'], - handler: ((editorState, menuService, context) async { - // Two cases - // 1. if there is content in the text node where the cursor is located, - // then we use the current text content as data. - // 2. if there is no content in the text node where the cursor is located, - // then we use the previous / next text node's content as data. - - final selection = - editorState.service.selectionService.currentSelection.value; - if (selection == null || !selection.isCollapsed) { - return; - } - - final textNodes = editorState.service.selectionService.currentSelectedNodes - .whereType(); - if (textNodes.isEmpty) { - return; - } - - final textRobot = TextRobot(editorState: editorState); - const gpt3 = GPT3APIClient(apiKey: apiKey); - final textNode = textNodes.first; - - var prompt = ''; - var suffix = ''; - - void continueToWriteInSingleLine() { - prompt = textNode.delta.slice(0, selection.startIndex).toPlainText(); - suffix = textNode.delta - .slice( - selection.endIndex, - textNode.toPlainText().length, - ) - .toPlainText(); - } - - void continueToWriteInMulitLines() { - final parent = textNode.parent; - if (parent != null) { - for (final node in parent.children) { - if (node is! TextNode || node.toPlainText().isEmpty) continue; - if (node.path < textNode.path) { - prompt += '${node.toPlainText()}\n'; - } else if (node.path > textNode.path) { - suffix += '${node.toPlainText()}\n'; - } - } - } - } - - if (textNodes.first.toPlainText().isNotEmpty) { - continueToWriteInSingleLine(); - } else { - continueToWriteInMulitLines(); - } - - if (prompt.isEmpty && suffix.isEmpty) { - return; - } - - late final BuildContext diglogContext; - - showDialog( - context: context, - builder: (context) { - diglogContext = context; - return AlertDialog( - content: Column( - mainAxisSize: MainAxisSize.min, - children: const [ - CircularProgressIndicator(), - SizedBox(height: 10), - Text('Loading'), - ], - ), - ); - }, - ); - - gpt3.getGPT3Completion( - prompt, - suffix, - onResult: (result) async { - Navigator.of(diglogContext).pop(true); - await textRobot.insertText( - result, - inputType: TextRobotInputType.word, - ); - }, - onError: () async { - Navigator.of(diglogContext).pop(true); - }, - ); - }), -); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/gpt3.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/gpt3.dart deleted file mode 100644 index a63f47ba3d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/gpt3.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'package:http/http.dart' as http; -import 'dart:async'; -import 'dart:convert'; - -// Please fill in your own API key -const apiKey = ''; - -enum GPT3API { - completion, - edit, -} - -extension on GPT3API { - Uri get uri { - switch (this) { - case GPT3API.completion: - return Uri.parse('https://api.openai.com/v1/completions'); - case GPT3API.edit: - return Uri.parse('https://api.openai.com/v1/edits'); - } - } -} - -class GPT3APIClient { - const GPT3APIClient({ - required this.apiKey, - }); - - final String apiKey; - - /// Get completions from GPT-3 - /// - /// [prompt] is the prompt text - /// [suffix] is the suffix text - /// [onResult] is the callback function to handle the result - /// [maxTokens] is the maximum number of tokens to generate - /// [temperature] is the temperature of the model - /// - /// See https://beta.openai.com/docs/api-reference/completions/create - Future getGPT3Completion( - String prompt, - String suffix, { - required Future Function(String result) onResult, - required Future Function() onError, - int maxTokens = 200, - double temperature = .3, - }) async { - final data = { - 'model': 'text-davinci-003', - 'prompt': prompt, - 'suffix': suffix, - 'max_tokens': maxTokens, - 'temperature': temperature, - 'stream': false, - }; - - final headers = { - 'Authorization': apiKey, - 'Content-Type': 'application/json', - }; - - final response = await http.post( - GPT3API.completion.uri, - headers: headers, - body: json.encode(data), - ); - - if (response.statusCode == 200) { - final result = json.decode(response.body); - final choices = result['choices']; - if (choices != null && choices is List) { - for (final choice in choices) { - final text = choice['text']; - await onResult(text); - } - } - } else { - await onError(); - } - } - - Future getGPT3Edit( - String apiKey, - String input, - String instruction, { - required Future Function(List result) onResult, - required Future Function() onError, - int n = 1, - double temperature = .3, - }) async { - final data = { - 'model': 'text-davinci-edit-001', - 'input': input, - 'instruction': instruction, - 'temperature': temperature, - 'n': n, - }; - - final headers = { - 'Authorization': apiKey, - 'Content-Type': 'application/json', - }; - - final response = await http.post( - Uri.parse('https://api.openai.com/v1/edits'), - headers: headers, - body: json.encode(data), - ); - if (response.statusCode == 200) { - final result = json.decode(response.body); - final choices = result['choices']; - if (choices != null && choices is List) { - await onResult(choices.map((e) => e['text'] as String).toList()); - } - } else { - await onError(); - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/smart_edit.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/smart_edit.dart deleted file mode 100644 index 49084cdd9d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/smart_edit.dart +++ /dev/null @@ -1,200 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:example/plugin/AI/gpt3.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -ToolbarItem smartEditItem = ToolbarItem( - id: 'appflowy.toolbar.smart_edit', - type: 5, - iconBuilder: (isHighlight) { - return Icon( - Icons.edit, - color: isHighlight ? Colors.lightBlue : Colors.white, - size: 14, - ); - }, - validator: (editorState) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - return nodes.whereType().length == nodes.length && - 1 == nodes.length; - }, - highlightCallback: (_) => false, - tooltipsMessage: 'Smart Edit', - handler: (editorState, context) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - content: SmartEditWidget( - editorState: editorState, - ), - ); - }, - ); - }, -); - -class SmartEditWidget extends StatefulWidget { - const SmartEditWidget({ - super.key, - required this.editorState, - }); - - final EditorState editorState; - - @override - State createState() => _SmartEditWidgetState(); -} - -class _SmartEditWidgetState extends State { - final inputEventController = TextEditingController(text: ''); - final resultController = TextEditingController(text: ''); - - var result = ''; - - final gpt3 = const GPT3APIClient(apiKey: apiKey); - - Iterable get currentSelectedTextNodes => - widget.editorState.service.selectionService.currentSelectedNodes - .whereType(); - Selection? get currentSelection => - widget.editorState.service.selectionService.currentSelection.value; - - @override - Widget build(BuildContext context) { - return Container( - constraints: const BoxConstraints(maxWidth: 400), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - RawKeyboardListener( - focusNode: FocusNode(), - child: TextField( - autofocus: true, - controller: inputEventController, - maxLines: null, - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: 'Describe how you\'d like AppFlowy to edit this text', - ), - ), - onKey: (key) { - if (key is! RawKeyDownEvent) return; - if (key.logicalKey == LogicalKeyboardKey.enter) { - _requestGPT3EditResult(); - } else if (key.logicalKey == LogicalKeyboardKey.escape) { - Navigator.of(context).pop(); - } - }, - ), - if (result.isNotEmpty) ...[ - const SizedBox(height: 20), - const Text( - 'Result: ', - style: TextStyle(color: Colors.grey), - ), - const SizedBox(height: 10), - SizedBox( - height: 300, - child: TextField( - controller: resultController..text = result, - maxLines: null, - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: - 'Describe how you\'d like AppFlowy to edit this text', - ), - ), - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.of(context).pop(); - - // replace the text - final selection = currentSelection; - if (selection != null) { - assert(selection.isSingle); - final transaction = widget.editorState.transaction; - transaction.replaceText( - currentSelectedTextNodes.first, - selection.startIndex, - selection.length, - resultController.text, - ); - widget.editorState.apply(transaction); - } - }, - child: const Text('Replace'), - ), - ], - ), - ] - ], - ), - ); - } - - void _requestGPT3EditResult() { - final selection = - widget.editorState.service.selectionService.currentSelection.value; - if (selection == null || !selection.isSingle) { - return; - } - final text = - widget.editorState.service.selectionService.currentSelectedNodes - .whereType() - .first - .delta - .slice( - selection.startIndex, - selection.endIndex, - ) - .toPlainText(); - if (text.isEmpty) { - Navigator.of(context).pop(); - return; - } - - showDialog( - context: context, - builder: (context) { - return AlertDialog( - content: Column( - mainAxisSize: MainAxisSize.min, - children: const [ - CircularProgressIndicator(), - SizedBox(height: 10), - Text('Loading'), - ], - ), - ); - }, - ); - - gpt3.getGPT3Edit( - apiKey, - text, - inputEventController.text, - onResult: (result) async { - Navigator.of(context).pop(true); - setState(() { - this.result = result.join('\n').trim(); - }); - }, - onError: () async { - Navigator.of(context).pop(true); - }, - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/text_robot.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/text_robot.dart deleted file mode 100644 index a3ac4adb58..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/AI/text_robot.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; - -enum TextRobotInputType { - character, - word, -} - -class TextRobot { - const TextRobot({ - required this.editorState, - this.delay = const Duration(milliseconds: 30), - }); - - final EditorState editorState; - final Duration delay; - - Future insertText( - String text, { - TextRobotInputType inputType = TextRobotInputType.character, - }) async { - final lines = text.split('\n'); - for (final line in lines) { - if (line.isEmpty) continue; - switch (inputType) { - case TextRobotInputType.character: - final iterator = line.runes.iterator; - while (iterator.moveNext()) { - await editorState.insertTextAtCurrentSelection( - iterator.currentAsString, - ); - await Future.delayed(delay, () {}); - } - break; - case TextRobotInputType.word: - final words = line.split(' ').map((e) => '$e '); - for (final word in words) { - await editorState.insertTextAtCurrentSelection( - word, - ); - await Future.delayed(delay, () {}); - } - break; - } - - // insert new line - if (lines.length > 1) { - await editorState.insertNewLineAtCurrentSelection(); - } - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/editor_theme.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/editor_theme.dart deleted file mode 100644 index be84ae38f9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/editor_theme.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; - -ThemeData customizeEditorTheme(BuildContext context) { - final dark = EditorStyle.dark; - final editorStyle = dark.copyWith( - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 150), - cursorColor: Colors.red.shade600, - selectionColor: Colors.yellow.shade600.withOpacity(0.5), - textStyle: GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.white, - ), - placeholderTextStyle: GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.grey.shade400, - ), - code: dark.code?.copyWith( - backgroundColor: Colors.lightBlue.shade200, - fontStyle: FontStyle.italic, - ), - highlightColorHex: '0x60FF0000', // red - ); - - final quote = QuotedTextPluginStyle.dark.copyWith( - textStyle: (_, __) => GoogleFonts.poppins().copyWith( - fontSize: 14, - color: Colors.blue.shade400, - fontStyle: FontStyle.italic, - fontWeight: FontWeight.w700, - ), - ); - - return Theme.of(context).copyWith(extensions: [ - editorStyle, - ...darkPluginStyleExtension, - quote, - ]); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/network_image_node_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/network_image_node_widget.dart deleted file mode 100644 index 395fc175f4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/lib/plugin/network_image_node_widget.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; - -class NetworkImageNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return _NetworkImageNodeWidget( - key: context.node.key, - node: context.node, - ); - } - - @override - NodeValidator get nodeValidator => (node) { - return node.type == 'network_image' && - node.attributes['network_image_src'] is String; - }; -} - -class _NetworkImageNodeWidget extends StatefulWidget { - const _NetworkImageNodeWidget({ - Key? key, - required this.node, - }) : super(key: key); - - final Node node; - - @override - State<_NetworkImageNodeWidget> createState() => - __NetworkImageNodeWidgetState(); -} - -class __NetworkImageNodeWidgetState extends State<_NetworkImageNodeWidget> - with SelectableMixin { - RenderBox get _renderBox => context.findRenderObject() as RenderBox; - - @override - Widget build(BuildContext context) { - return Image.network( - widget.node.attributes['network_image_src'], - height: 200, - loadingBuilder: (context, child, loadingProgress) => - loadingProgress == null ? child : const CircularProgressIndicator(), - ); - } - - @override - Position start() => Position(path: widget.node.path, offset: 0); - - @override - Position end() => Position(path: widget.node.path, offset: 1); - - @override - Position getPositionInOffset(Offset start) => end(); - - @override - List getRectsInSelection(Selection selection) => - [Offset.zero & _renderBox.size]; - - @override - Selection getSelectionInRange(Offset start, Offset end) => Selection.single( - path: widget.node.path, - startOffset: 0, - endOffset: 1, - ); - - @override - Offset localToGlobal(Offset offset) => _renderBox.localToGlobal(offset); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/.gitignore b/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/.gitignore deleted file mode 100644 index d3896c9844..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flutter/ephemeral diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/CMakeLists.txt b/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/CMakeLists.txt deleted file mode 100644 index 74c66dd446..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/CMakeLists.txt +++ /dev/null @@ -1,138 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "example") -# The unique GTK application identifier for this application. See: -# https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.example") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Load bundled libraries from the lib/ directory relative to the binary. -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Root filesystem for cross-building. -if(FLUTTER_TARGET_PLATFORM_SYSROOT) - set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endif() - -# Define build configuration options. -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") -endif() - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) - target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") - target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) - -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Define the application target. To change its name, change BINARY_NAME above, -# not the value here, or `flutter run` will no longer work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add dependency libraries. Add any application-specific dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) - -# Only the install-generated bundle's copy of the executable will launch -# correctly, since the resources must in the right relative locations. To avoid -# people trying to run the unbundled copy, put it in a subdirectory instead of -# the default top-level location. -set_target_properties(${BINARY_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" -) - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# By default, "installing" just makes a relocatable bundle in the build -# directory. -set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -# Start with a clean build bundle directory every time. -install(CODE " - file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") - " COMPONENT Runtime) - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) - install(FILES "${bundled_library}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endforeach(bundled_library) - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") - install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/flutter/CMakeLists.txt b/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/flutter/CMakeLists.txt deleted file mode 100644 index d5bd01648a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/flutter/CMakeLists.txt +++ /dev/null @@ -1,88 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.10) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. - -# Serves the same purpose as list(TRANSFORM ... PREPEND ...), -# which isn't available in 3.10. -function(list_prepend LIST_NAME PREFIX) - set(NEW_LIST "") - foreach(element ${${LIST_NAME}}) - list(APPEND NEW_LIST "${PREFIX}${element}") - endforeach(element) - set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) -endfunction() - -# === Flutter Library === -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) -pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) - -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "fl_basic_message_channel.h" - "fl_binary_codec.h" - "fl_binary_messenger.h" - "fl_dart_project.h" - "fl_engine.h" - "fl_json_message_codec.h" - "fl_json_method_codec.h" - "fl_message_codec.h" - "fl_method_call.h" - "fl_method_channel.h" - "fl_method_codec.h" - "fl_method_response.h" - "fl_plugin_registrar.h" - "fl_plugin_registry.h" - "fl_standard_message_codec.h" - "fl_standard_method_codec.h" - "fl_string_codec.h" - "fl_value.h" - "fl_view.h" - "flutter_linux.h" -) -list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") -target_link_libraries(flutter INTERFACE - PkgConfig::GTK - PkgConfig::GLIB - PkgConfig::GIO -) -add_dependencies(flutter flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} -) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/main.cc b/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/main.cc deleted file mode 100644 index e7c5c54370..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "my_application.h" - -int main(int argc, char** argv) { - g_autoptr(MyApplication) app = my_application_new(); - return g_application_run(G_APPLICATION(app), argc, argv); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/my_application.cc b/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/my_application.cc deleted file mode 100644 index 0ba8f43096..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/my_application.cc +++ /dev/null @@ -1,104 +0,0 @@ -#include "my_application.h" - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#include "flutter/generated_plugin_registrant.h" - -struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; -}; - -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "example"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } else { - gtk_window_set_title(window, "example"); - } - - gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); - - g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - - FlView* view = fl_view_new(project); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -// Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); - // Strip out the first argument as it is the binary name. - self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - - g_autoptr(GError) error = nullptr; - if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; - } - - g_application_activate(application); - *exit_status = 0; - - return TRUE; -} - -// Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); - g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); - G_OBJECT_CLASS(my_application_parent_class)->dispose(object); -} - -static void my_application_class_init(MyApplicationClass* klass) { - G_APPLICATION_CLASS(klass)->activate = my_application_activate; - G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; - G_OBJECT_CLASS(klass)->dispose = my_application_dispose; -} - -static void my_application_init(MyApplication* self) {} - -MyApplication* my_application_new() { - return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, - nullptr)); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/my_application.h b/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/my_application.h deleted file mode 100644 index 72271d5e41..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/linux/my_application.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_MY_APPLICATION_H_ -#define FLUTTER_MY_APPLICATION_H_ - -#include - -G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, - GtkApplication) - -/** - * my_application_new: - * - * Creates a new Flutter-based application. - * - * Returns: a new #MyApplication. - */ -MyApplication* my_application_new(); - -#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/.gitignore b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/.gitignore deleted file mode 100644 index 746adbb6b9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Flutter/Flutter-Debug.xcconfig b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index 4b81f9b2d2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Flutter/Flutter-Release.xcconfig b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index 5caa9d1579..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Podfile b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Podfile deleted file mode 100644 index dade8dfad0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Podfile +++ /dev/null @@ -1,40 +0,0 @@ -platform :osx, '10.11' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Podfile.lock b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Podfile.lock deleted file mode 100644 index cacc879cd6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Podfile.lock +++ /dev/null @@ -1,48 +0,0 @@ -PODS: - - flowy_infra_ui (0.0.1): - - FlutterMacOS - - FlutterMacOS (1.0.0) - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - - rich_clipboard_macos (0.0.1): - - FlutterMacOS - - shared_preferences_foundation (0.0.1): - - Flutter - - FlutterMacOS - - url_launcher_macos (0.0.1): - - FlutterMacOS - -DEPENDENCIES: - - flowy_infra_ui (from `Flutter/ephemeral/.symlinks/plugins/flowy_infra_ui/macos`) - - FlutterMacOS (from `Flutter/ephemeral`) - - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) - - rich_clipboard_macos (from `Flutter/ephemeral/.symlinks/plugins/rich_clipboard_macos/macos`) - - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) - - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - -EXTERNAL SOURCES: - flowy_infra_ui: - :path: Flutter/ephemeral/.symlinks/plugins/flowy_infra_ui/macos - FlutterMacOS: - :path: Flutter/ephemeral - path_provider_foundation: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos - rich_clipboard_macos: - :path: Flutter/ephemeral/.symlinks/plugins/rich_clipboard_macos/macos - shared_preferences_foundation: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos - url_launcher_macos: - :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - -SPEC CHECKSUMS: - flowy_infra_ui: c34d49d615ed9fe552cd47f90d7850815a74e9e9 - FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 - path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 - rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c - shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 - url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 - -PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c - -COCOAPODS: 1.11.3 diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/project.pbxproj b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 057a1a8224..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,632 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 8FD791997F0D60CE136153FB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F21284F13DB2F7E10C6EB1F7 /* Pods_Runner.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 4C1351C0AA74138239028404 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - BBAF6135AB8D71FE6D8B315C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - BE3A038D8FDF07F3AD1C02FB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - F21284F13DB2F7E10C6EB1F7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8FD791997F0D60CE136153FB /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - 7B5E3B15415D0C17244EF9E7 /* Pods */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* example.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - 7B5E3B15415D0C17244EF9E7 /* Pods */ = { - isa = PBXGroup; - children = ( - BBAF6135AB8D71FE6D8B315C /* Pods-Runner.debug.xcconfig */, - 4C1351C0AA74138239028404 /* Pods-Runner.release.xcconfig */, - BE3A038D8FDF07F3AD1C02FB /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - F21284F13DB2F7E10C6EB1F7 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 87BB3D0057F20B3618A17B82 /* [CP] Check Pods Manifest.lock */, - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - 09CDF3F9864A27F94DEE8EC6 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* example.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 09CDF3F9864A27F94DEE8EC6 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; - 87BB3D0057F20B3618A17B82 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index fb7259e177..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c7..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/AppDelegate.swift b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/AppDelegate.swift deleted file mode 100644 index d53ef64377..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f19f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 3c4935a7ca..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index ed4cc16421..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 483be61389..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bcbf36df2f..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index 9c0a652864..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index e71a726136..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 8a31fe2dd3..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Base.lproj/MainMenu.xib b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Base.lproj/MainMenu.xib deleted file mode 100644 index a6d4312ff0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,344 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/AppInfo.xcconfig b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 8b42559e87..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = example - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.example - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved. diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Debug.xcconfig b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd9464..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Release.xcconfig b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f49561..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Warnings.xcconfig b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf4780..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/DebugProfile.entitlements b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index c946719a1a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,14 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - com.apple.security.network.client - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Info.plist b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6a4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/MainFlutterWindow.swift b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 2722837ec9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Release.entitlements b/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Release.entitlements deleted file mode 100644 index 48271acc95..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Runner/Release.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.network.client - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/pubspec.yaml b/frontend/appflowy_flutter/packages/appflowy_editor/example/pubspec.yaml deleted file mode 100644 index ab0eb3aa7b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/pubspec.yaml +++ /dev/null @@ -1,102 +0,0 @@ -name: example -description: A new Flutter project. - -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: "none" # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 - -environment: - sdk: ">=2.17.0 <3.0.0" - -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. -dependencies: - flutter: - sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - appflowy_editor: - path: ../ - provider: ^6.0.3 - url_launcher: ^6.1.5 - path_provider: ^2.0.11 - google_fonts: ^3.0.1 - flutter_localizations: - sdk: flutter - file_picker: ^5.0.1 - universal_html: ^2.0.8 - highlight: ^0.7.0 - flutter_math_fork: ^0.6.3+1 - appflowy_editor_plugins: - path: ../../../packages/appflowy_editor_plugins - http: ^0.13.5 - -dev_dependencies: - flutter_test: - sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.1 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - assets: - - example.json - - assets/images/icon.png - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/test/widget_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/example/test/widget_test.dart deleted file mode 100644 index 2a2b819285..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/test/widget_test.dart +++ /dev/null @@ -1,8 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -void main() {} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/favicon.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/web/favicon.png deleted file mode 100644 index 8aaa46ac1a..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/favicon.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-192.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-192.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-512.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-512.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-maskable-192.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76e5..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-maskable-512.png b/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c56691f..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/index.html b/frontend/appflowy_flutter/packages/appflowy_editor/example/web/index.html deleted file mode 100644 index 41b3bc336f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/index.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - example - - - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/manifest.json b/frontend/appflowy_flutter/packages/appflowy_editor/example/web/manifest.json deleted file mode 100644 index 096edf8fe4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "example", - "short_name": "example", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/.gitignore b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/.gitignore deleted file mode 100644 index d492d0d98c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/CMakeLists.txt b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/CMakeLists.txt deleted file mode 100644 index c0270746b1..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/CMakeLists.txt +++ /dev/null @@ -1,101 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.14) -project(example LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "example") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Define build configuration option. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() -# Define settings for the Profile build mode. -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build; see runner/CMakeLists.txt. -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/flutter/CMakeLists.txt b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/flutter/CMakeLists.txt deleted file mode 100644 index 930d2071a3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,104 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/CMakeLists.txt b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/CMakeLists.txt deleted file mode 100644 index b9e550fba8..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -# Define the application target. To change its name, change BINARY_NAME in the -# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer -# work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Disable Windows macros that collide with C++ standard library functions. -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") - -# Add dependency libraries and include directories. Add any application-specific -# dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/Runner.rc b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/Runner.rc deleted file mode 100644 index 5fdea291cf..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER -#else -#define VERSION_AS_NUMBER 1,0,0 -#endif - -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "example" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "example.exe" "\0" - VALUE "ProductName", "example" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/flutter_window.cpp b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/flutter_window.cpp deleted file mode 100644 index b43b9095ea..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/flutter_window.h b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/flutter_window.h deleted file mode 100644 index 6da0652f05..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/flutter_window.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/main.cpp b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/main.cpp deleted file mode 100644 index bcb57b0e2a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"example", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/resource.h b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/resource.h deleted file mode 100644 index 66a65d1e4a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/resources/app_icon.ico b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6..0000000000 Binary files a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/resources/app_icon.ico and /dev/null differ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/runner.exe.manifest b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/runner.exe.manifest deleted file mode 100644 index c977c4a425..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/utils.cpp b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/utils.cpp deleted file mode 100644 index f5bf9fa0f5..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/utils.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - std::string utf8_string; - if (target_length == 0 || target_length > utf8_string.max_size()) { - return utf8_string; - } - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/utils.h b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/utils.h deleted file mode 100644 index 3879d54755..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/win32_window.cpp b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/win32_window.cpp deleted file mode 100644 index c10f08dc7d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/win32_window.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "win32_window.h" - -#include - -#include "resource.h" - -namespace { - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); - } -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - return OnCreate(); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/win32_window.h b/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/win32_window.h deleted file mode 100644 index 17ba431125..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/example/windows/runner/win32_window.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates and shows a win32 window with |title| and position and size using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/appflowy_editor.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/appflowy_editor.dart deleted file mode 100644 index 39eca0b904..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/appflowy_editor.dart +++ /dev/null @@ -1,51 +0,0 @@ -/// AppFlowyEditor library -library appflowy_editor; - -export 'src/infra/log.dart'; -export 'src/render/style/editor_style.dart'; -export 'src/core/document/node.dart'; -export 'src/core/document/path.dart'; -export 'src/core/location/position.dart'; -export 'src/core/location/selection.dart'; -export 'src/core/document/document.dart'; -export 'src/core/document/text_delta.dart'; -export 'src/core/document/attributes.dart'; -export 'src/core/legacy/built_in_attribute_keys.dart'; -export 'src/editor_state.dart'; -export 'src/core/transform/operation.dart'; -export 'src/core/transform/transaction.dart'; -export 'src/render/selection/selectable.dart'; -export 'src/render/selection_menu/selection_menu_service.dart'; -export 'src/service/editor_service.dart'; -export 'src/service/render_plugin_service.dart'; -export 'src/service/service.dart'; -export 'src/service/selection_service.dart'; -export 'src/service/scroll_service.dart'; -export 'src/service/toolbar_service.dart'; -export 'src/service/keyboard_service.dart'; -export 'src/service/input_service.dart'; -export 'src/service/shortcut_event/keybinding.dart'; -export 'src/service/shortcut_event/shortcut_event.dart'; -export 'src/service/shortcut_event/shortcut_event_handler.dart'; -export 'src/extensions/attributes_extension.dart'; -export 'src/render/rich_text/default_selectable.dart'; -export 'src/render/rich_text/flowy_rich_text.dart'; -export 'src/render/selection_menu/selection_menu_widget.dart'; -export 'src/render/selection_menu/selection_menu_item_widget.dart'; -export 'src/l10n/l10n.dart'; -export 'src/render/style/plugin_styles.dart'; -export 'src/plugins/markdown/encoder/delta_markdown_encoder.dart'; -export 'src/plugins/markdown/encoder/document_markdown_encoder.dart'; -export 'src/plugins/markdown/encoder/parser/node_parser.dart'; -export 'src/plugins/markdown/encoder/parser/text_node_parser.dart'; -export 'src/plugins/markdown/encoder/parser/image_node_parser.dart'; -export 'src/plugins/markdown/decoder/delta_markdown_decoder.dart'; -export 'src/plugins/markdown/document_markdown.dart'; -export 'src/plugins/quill_delta/delta_document_encoder.dart'; -export 'src/commands/text/text_commands.dart'; -export 'src/commands/command_extension.dart'; -export 'src/render/toolbar/toolbar_item.dart'; -export 'src/extensions/node_extensions.dart'; -export 'src/render/action_menu/action_menu.dart'; -export 'src/render/action_menu/action_menu_item.dart'; -export 'src/core/document/node_iterator.dart'; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_bn_BN.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_bn_BN.arb deleted file mode 100644 index fa58561498..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_bn_BN.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "bn_BN", - "bold": "বল্ড ফন্ট", - "@bold": {}, - "bulletedList": "বুলেট তালিকা", - "@bulletedList": {}, - "checkbox": "চেকবক্স", - "@checkbox": {}, - "embedCode": "এম্বেড কোড", - "@embedCode": {}, - "heading1": "শিরোনাম 1", - "@heading1": {}, - "heading2": "শিরোনাম 2", - "@heading2": {}, - "heading3": "শিরোনাম 3", - "@heading3": {}, - "highlight": "হাইলাইট", - "@highlight": {}, - "image": "ইমেজ", - "@image": {}, - "italic": "ইটালিক ফন্ট", - "@italic": {}, - "link": "লিঙ্ক", - "@link": {}, - "numberedList": "সংখ্যাযুক্ত তালিকা", - "@numberedList": {}, - "quote": "উদ্ধৃতি", - "@quote": {}, - "strikethrough": "স্ট্রাইকথ্রু", - "@strikethrough": {}, - "text": "পাঠ্য", - "@text": {}, - "underline": "আন্ডারলাইন", - "@underline": {} -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ca.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ca.arb deleted file mode 100644 index 45190fb106..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ca.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "ca", - "bold": "", - "@bold": {}, - "bulletedList": "", - "@bulletedList": {}, - "checkbox": "", - "@checkbox": {}, - "embedCode": "", - "@embedCode": {}, - "heading1": "", - "@heading1": {}, - "heading2": "", - "@heading2": {}, - "heading3": "", - "@heading3": {}, - "highlight": "", - "@highlight": {}, - "image": "", - "@image": {}, - "italic": "", - "@italic": {}, - "link": "", - "@link": {}, - "numberedList": "", - "@numberedList": {}, - "quote": "", - "@quote": {}, - "strikethrough": "", - "@strikethrough": {}, - "text": "", - "@text": {}, - "underline": "", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_cs_CZ.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_cs_CZ.arb deleted file mode 100644 index 2b1ea7b0bf..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_cs_CZ.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "cs-CZ", - "bold": "Tučně", - "@bold": {}, - "bulletedList": "Odrážkový seznam", - "@bulletedList": {}, - "checkbox": "Zaškrtávací políčko", - "@checkbox": {}, - "embedCode": "Vložit kód", - "@embedCode": {}, - "heading1": "Nadpis 1", - "@heading1": {}, - "heading2": "Nadpis 2", - "@heading2": {}, - "heading3": "Nadpis 3", - "@heading3": {}, - "highlight": "Zvýraznění", - "@highlight": {}, - "image": "Obrázek", - "@image": {}, - "italic": "Kurzíva", - "@italic": {}, - "link": "Odkaz", - "@link": {}, - "numberedList": "Číslovaný seznam", - "@numberedList": {}, - "quote": "Citace", - "@quote": {}, - "strikethrough": "Přeškrtnutí", - "@strikethrough": {}, - "text": "Text", - "@text": {}, - "underline": "Podtržení", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_de_DE.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_de_DE.arb deleted file mode 100644 index bd39879f0d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_de_DE.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "de-DE", - "bold": "Fett gedruckt", - "@bold": {}, - "bulletedList": "Aufzählungsliste", - "@bulletedList": {}, - "checkbox": "Kontrollkästchen", - "@checkbox": {}, - "embedCode": "Code einbetten", - "@embedCode": {}, - "heading1": "Überschrift 1", - "@heading1": {}, - "heading2": "Überschrift 2", - "@heading2": {}, - "heading3": "Überschrift 3", - "@heading3": {}, - "highlight": "Markieren", - "@highlight": {}, - "image": "Bild", - "@image": {}, - "italic": "kursiv", - "@italic": {}, - "link": "Verknüpfung", - "@link": {}, - "numberedList": "NummerierteListe", - "@numberedList": {}, - "quote": "zitieren", - "@quote": {}, - "strikethrough": "durchgestrichen", - "@strikethrough": {}, - "text": "Text", - "@text": {}, - "underline": "unterstreichen", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_en.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_en.arb deleted file mode 100644 index 6e8575552f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_en.arb +++ /dev/null @@ -1,95 +0,0 @@ -{ - "@@locale": "en", - "bold": "Bold", - "@bold": {}, - "bulletedList": "Bulleted List", - "@bulletedList": {}, - "checkbox": "Checkbox", - "@checkbox": {}, - "embedCode": "Embed Code", - "@embedCode": {}, - "heading1": "H1", - "@heading1": {}, - "heading2": "H2", - "@heading2": {}, - "heading3": "H3", - "@heading3": {}, - "highlight": "Highlight", - "@highlight": {}, - "color": "Color", - "@color": {}, - "image": "Image", - "@image": {}, - "italic": "Italic", - "@italic": {}, - "link": "Link", - "@link": {}, - "numberedList": "Numbered List", - "@numberedList": {}, - "quote": "Quote", - "@quote": {}, - "strikethrough": "Strikethrough", - "@strikethrough": {}, - "text": "Text", - "@text": {}, - "underline": "Underline", - "@underline": {}, - "fontColorDefault": "Default", - "@fontColorDefault": {}, - "fontColorGray": "Gray", - "@fontColorGray": {}, - "fontColorBrown": "Brown", - "@fontColorBrown": {}, - "fontColorOrange": "Orange", - "@fontColorOrange": {}, - "fontColorYellow": "Yellow", - "@fontColorYellow": {}, - "fontColorGreen": "Green", - "@fontColorGreen": {}, - "fontColorBlue": "Blue", - "@fontColorBlue": {}, - "fontColorPurple": "Purple", - "@fontColorPurple": {}, - "fontColorPink": "Pink", - "@fontColorPink": {}, - "fontColorRed": "Red", - "@fontColorRed": {}, - "backgroundColorDefault": "Default background", - "@backgroundColorDefault": {}, - "backgroundColorGray": "Gray background", - "@backgroundColorGray": {}, - "backgroundColorBrown": "Brown background", - "@backgroundColorBrown": {}, - "backgroundColorOrange": "Orange background", - "@backgroundColorOrange": {}, - "backgroundColorYellow": "Yellow background", - "@backgroundColorYellow": {}, - "backgroundColorGreen": "Green background", - "@backgroundColorGreen": {}, - "backgroundColorBlue": "Blue background", - "@backgroundColorBlue": {}, - "backgroundColorPurple": "Purple background", - "@backgroundColorPurple": {}, - "backgroundColorPink": "Pink background", - "@backgroundColorPink": {}, - "backgroundColorRed": "Red background", - "@backgroundColorRed": {}, - "tint1": "Tint 1", - "tint2": "Tint 2", - "tint3": "Tint 3", - "tint4": "Tint 4", - "tint5": "Tint 5", - "tint6": "Tint 6", - "tint7": "Tint 7", - "tint8": "Tint 8", - "tint9": "Tint 9", - "lightLightTint1": "Purple", - "lightLightTint2": "Pink", - "lightLightTint3": "Light Pink", - "lightLightTint4": "Orange", - "lightLightTint5": "Yellow", - "lightLightTint6": "Lime", - "lightLightTint7": "Green", - "lightLightTint8": "Aqua", - "lightLightTint9": "Blue" -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_es_VE.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_es_VE.arb deleted file mode 100644 index bace9331ae..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_es_VE.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "es-VE", - "bold": "", - "@bold": {}, - "bulletedList": "", - "@bulletedList": {}, - "checkbox": "", - "@checkbox": {}, - "embedCode": "", - "@embedCode": {}, - "heading1": "", - "@heading1": {}, - "heading2": "", - "@heading2": {}, - "heading3": "", - "@heading3": {}, - "highlight": "", - "@highlight": {}, - "image": "", - "@image": {}, - "italic": "", - "@italic": {}, - "link": "", - "@link": {}, - "numberedList": "", - "@numberedList": {}, - "quote": "", - "@quote": {}, - "strikethrough": "", - "@strikethrough": {}, - "text": "", - "@text": {}, - "underline": "", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb deleted file mode 100644 index 1ff4ab2f75..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "fr-CA", - "bold": "gras", - "@bold": {}, - "bulletedList": "liste à puces", - "@bulletedList": {}, - "checkbox": "case à cocher", - "@checkbox": {}, - "embedCode": "incorporer Code", - "@embedCode": {}, - "heading1": "en-tête1", - "@heading1": {}, - "heading2": "en-tête2", - "@heading2": {}, - "heading3": "en-tête3", - "@heading3": {}, - "highlight": "mettre en évidence", - "@highlight": {}, - "image": "l’image", - "@image": {}, - "italic": "italique", - "@italic": {}, - "link": "lien", - "@link": {}, - "numberedList": "liste numérotée", - "@numberedList": {}, - "quote": "citation", - "@quote": {}, - "strikethrough": "barré", - "@strikethrough": {}, - "text": "texte", - "@text": {}, - "underline": "souligner", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb deleted file mode 100644 index 0e63189506..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "fr-FR", - "bold": "Gras", - "@bold": {}, - "bulletedList": "List à puces", - "@bulletedList": {}, - "checkbox": "Case à cocher", - "@checkbox": {}, - "embedCode": "Incorporer code", - "@embedCode": {}, - "heading1": "Titre 1", - "@heading1": {}, - "heading2": "Titre 2", - "@heading2": {}, - "heading3": "Titre 3", - "@heading3": {}, - "highlight": "Surligné", - "@highlight": {}, - "image": "Image", - "@image": {}, - "italic": "Italique", - "@italic": {}, - "link": "Lien", - "@link": {}, - "numberedList": "Liste numérotée", - "@numberedList": {}, - "quote": "Citation", - "@quote": {}, - "strikethrough": "Barré", - "@strikethrough": {}, - "text": "Texte", - "@text": {}, - "underline": "Souligné", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_hi_IN.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_hi_IN.arb deleted file mode 100644 index 96102143c1..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_hi_IN.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "hi-IN", - "bold": "बोल्ड", - "@bold": {}, - "bulletedList": "बुलेटेड सूची", - "@bulletedList": {}, - "checkbox": "चेक बॉक्स", - "@checkbox": {}, - "embedCode": "लागु किया गया संहिता", - "@embedCode": {}, - "heading1": "शीर्षक 1", - "@heading1": {}, - "heading2": "शीर्षक 2", - "@heading2": {}, - "heading3": "शीर्षक 3", - "@heading3": {}, - "highlight": "प्रमुखता से दिखाना", - "@highlight": {}, - "image": "छवि", - "@image": {}, - "italic": "तिरछा", - "@italic": {}, - "link": "संपर्क", - "@link": {}, - "numberedList": "क्रमांकित सूची", - "@numberedList": {}, - "quote": "उद्धरण", - "@quote": {}, - "strikethrough": "स्ट्राइकथ्रू", - "@strikethrough": {}, - "text": "मूलपाठ", - "@text": {}, - "underline": "रेखांकन", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb deleted file mode 100644 index f96b3b0ec3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "hu-HU", - "bold": "bátor", - "@bold": {}, - "bulletedList": "pontozott lista", - "@bulletedList": {}, - "checkbox": "jelölőnégyzetet", - "@checkbox": {}, - "embedCode": "Beágyazás", - "@embedCode": {}, - "heading1": "címsor1", - "@heading1": {}, - "heading2": "címsor2", - "@heading2": {}, - "heading3": "címsor3", - "@heading3": {}, - "highlight": "Kiemel", - "@highlight": {}, - "image": "kép", - "@image": {}, - "italic": "dőlt", - "@italic": {}, - "link": "link", - "@link": {}, - "numberedList": "számozottLista", - "@numberedList": {}, - "quote": "idézet", - "@quote": {}, - "strikethrough": "áthúzott", - "@strikethrough": {}, - "text": "szöveg", - "@text": {}, - "underline": "aláhúzás", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_id_ID.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_id_ID.arb deleted file mode 100644 index 6b3f6a0a6d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_id_ID.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "id-ID", - "bold": "berani", - "@bold": {}, - "bulletedList": "daftar berpoin", - "@bulletedList": {}, - "checkbox": "kotak centang", - "@checkbox": {}, - "embedCode": "menyematkan Kode", - "@embedCode": {}, - "heading1": "pos1", - "@heading1": {}, - "heading2": "pos2", - "@heading2": {}, - "heading3": "pos3", - "@heading3": {}, - "highlight": "menyorot", - "@highlight": {}, - "image": "gambar", - "@image": {}, - "italic": "miring", - "@italic": {}, - "link": "tautan", - "@link": {}, - "numberedList": "daftar bernomor", - "@numberedList": {}, - "quote": "mengutip", - "@quote": {}, - "strikethrough": "coret", - "@strikethrough": {}, - "text": "teks", - "@text": {}, - "underline": "menggarisbawahi", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_it_IT.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_it_IT.arb deleted file mode 100644 index e645959f7b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_it_IT.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "it-IT", - "bold": "Grassetto", - "@bold": {}, - "bulletedList": "Elenco puntato", - "@bulletedList": {}, - "checkbox": "Casella di spunta", - "@checkbox": {}, - "embedCode": "Incorpora codice", - "@embedCode": {}, - "heading1": "H1", - "@heading1": {}, - "heading2": "H2", - "@heading2": {}, - "heading3": "H3", - "@heading3": {}, - "highlight": "Evidenzia", - "@highlight": {}, - "image": "Immagine", - "@image": {}, - "italic": "Corsivo", - "@italic": {}, - "link": "Collegamento", - "@link": {}, - "numberedList": "Elenco numerato", - "@numberedList": {}, - "quote": "Cita", - "@quote": {}, - "strikethrough": "Barrato", - "@strikethrough": {}, - "text": "Testo", - "@text": {}, - "underline": "Sottolineato", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ja_JP.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ja_JP.arb deleted file mode 100644 index 5d49578cc3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ja_JP.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "ja-JP", - "bold": "", - "@bold": {}, - "bulletedList": "", - "@bulletedList": {}, - "checkbox": "", - "@checkbox": {}, - "embedCode": "", - "@embedCode": {}, - "heading1": "", - "@heading1": {}, - "heading2": "", - "@heading2": {}, - "heading3": "", - "@heading3": {}, - "highlight": "", - "@highlight": {}, - "image": "", - "@image": {}, - "italic": "", - "@italic": {}, - "link": "", - "@link": {}, - "numberedList": "", - "@numberedList": {}, - "quote": "", - "@quote": {}, - "strikethrough": "", - "@strikethrough": {}, - "text": "", - "@text": {}, - "underline": "", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ml_IN.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ml_IN.arb deleted file mode 100644 index d48179e149..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ml_IN.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "ml_IN", - "bold": "ബോൾഡ്", - "@bold": {}, - "bulletedList": "ബുള്ളറ്റഡ് പട്ടിക", - "@bulletedList": {}, - "checkbox": "ചെക്ക്ബോക്സ്", - "@checkbox": {}, - "embedCode": "എംബെഡഡ് കോഡ്", - "@embedCode": {}, - "heading1": "തലക്കെട്ട് 1", - "@heading1": {}, - "heading2": "തലക്കെട്ട് 2", - "@heading2": {}, - "heading3": "തലക്കെട്ട് 3", - "@heading3": {}, - "highlight": "പ്രമുഖമാക്കിക്കാട്ടുക", - "@highlight": {}, - "image": "ചിത്രം", - "@image": {}, - "italic": "ഇറ്റാലിക്", - "@italic": {}, - "link": "ലിങ്ക്", - "@link": {}, - "numberedList": "അക്കമിട്ട പട്ടിക", - "@numberedList": {}, - "quote": "ഉദ്ധരണി", - "@quote": {}, - "strikethrough": "സ്ട്രൈക്ക്ത്രൂ", - "@strikethrough": {}, - "text": "വചനം", - "@text": {}, - "underline": "അടിവരയിടുക", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_nl_NL.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_nl_NL.arb deleted file mode 100644 index 814e22655d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_nl_NL.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "nl-NL", - "bold": "Vet", - "@bold": {}, - "bulletedList": "Opsommingstekens", - "@bulletedList": {}, - "checkbox": "Selectievakje", - "@checkbox": {}, - "embedCode": "Invoegcode", - "@embedCode": {}, - "heading1": "H1", - "@heading1": {}, - "heading2": "H2", - "@heading2": {}, - "heading3": "H3", - "@heading3": {}, - "highlight": "Highlight", - "@highlight": {}, - "image": "Afbeelding", - "@image": {}, - "italic": "Cursief", - "@italic": {}, - "link": "", - "@link": {}, - "numberedList": "Nummering", - "@numberedList": {}, - "quote": "Quote", - "@quote": {}, - "strikethrough": "Doorhalen", - "@strikethrough": {}, - "text": "Tekst", - "@text": {}, - "underline": "Onderstrepen", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pl_PL.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pl_PL.arb deleted file mode 100644 index ba97af8f26..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pl_PL.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "pl-PL", - "bold": "", - "@bold": {}, - "bulletedList": "", - "@bulletedList": {}, - "checkbox": "", - "@checkbox": {}, - "embedCode": "", - "@embedCode": {}, - "heading1": "", - "@heading1": {}, - "heading2": "", - "@heading2": {}, - "heading3": "", - "@heading3": {}, - "highlight": "", - "@highlight": {}, - "image": "", - "@image": {}, - "italic": "", - "@italic": {}, - "link": "", - "@link": {}, - "numberedList": "", - "@numberedList": {}, - "quote": "", - "@quote": {}, - "strikethrough": "", - "@strikethrough": {}, - "text": "", - "@text": {}, - "underline": "", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pt_BR.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pt_BR.arb deleted file mode 100644 index 8f9186b7d8..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pt_BR.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "pt-BR", - "bold": "Negrito", - "@bold": {}, - "bulletedList": "Lista de marcadores", - "@bulletedList": {}, - "checkbox": "Caixa de seleção", - "@checkbox": {}, - "embedCode": "Código incorporado", - "@embedCode": {}, - "heading1": "H1", - "@heading1": {}, - "heading2": "H2", - "@heading2": {}, - "heading3": "H3", - "@heading3": {}, - "highlight": "Destacar", - "@highlight": {}, - "image": "Imagem", - "@image": {}, - "italic": "Itálico", - "@italic": {}, - "link": "Link", - "@link": {}, - "numberedList": "Lista numerada", - "@numberedList": {}, - "quote": "Citar", - "@quote": {}, - "strikethrough": "Rasurar", - "@strikethrough": {}, - "text": "Texto", - "@text": {}, - "underline": "Sublinhar", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb deleted file mode 100644 index 9b7386fd46..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "pt-PT", - "bold": "negrito", - "@bold": {}, - "bulletedList": "lista com marcadores", - "@bulletedList": {}, - "checkbox": "caixa de seleção", - "@checkbox": {}, - "embedCode": "Código embutido", - "@embedCode": {}, - "heading1": "Cabeçallho 1", - "@heading1": {}, - "heading2": "Cabeçallho 2", - "@heading2": {}, - "heading3": "Cabeçallho 3", - "@heading3": {}, - "highlight": "realçar", - "@highlight": {}, - "image": "imagem", - "@image": {}, - "italic": "itálico", - "@italic": {}, - "link": "link", - "@link": {}, - "numberedList": "lista numerada", - "@numberedList": {}, - "quote": "citar", - "@quote": {}, - "strikethrough": "tachado", - "@strikethrough": {}, - "text": "texto", - "@text": {}, - "underline": "sublinhado", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ru_RU.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ru_RU.arb deleted file mode 100644 index 33408dcaa0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_ru_RU.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "ru-RU", - "bold": "смелый", - "@bold": {}, - "bulletedList": "маркированный список", - "@bulletedList": {}, - "checkbox": "флажок", - "@checkbox": {}, - "embedCode": "код для вставки", - "@embedCode": {}, - "heading1": "заголовок1", - "@heading1": {}, - "heading2": "заголовок2", - "@heading2": {}, - "heading3": "заголовок3", - "@heading3": {}, - "highlight": "выделять", - "@highlight": {}, - "image": "изображение", - "@image": {}, - "italic": "курсив", - "@italic": {}, - "link": "ссылка на сайт", - "@link": {}, - "numberedList": "нумерованный список", - "@numberedList": {}, - "quote": "цитировать", - "@quote": {}, - "strikethrough": "зачеркнутый", - "@strikethrough": {}, - "text": "текст", - "@text": {}, - "underline": "подчеркнуть", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_tr_TR.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_tr_TR.arb deleted file mode 100644 index 16887cd1cf..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_tr_TR.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "tr-TR", - "bold": "", - "@bold": {}, - "bulletedList": "", - "@bulletedList": {}, - "checkbox": "", - "@checkbox": {}, - "embedCode": "", - "@embedCode": {}, - "heading1": "", - "@heading1": {}, - "heading2": "", - "@heading2": {}, - "heading3": "", - "@heading3": {}, - "highlight": "", - "@highlight": {}, - "image": "", - "@image": {}, - "italic": "", - "@italic": {}, - "link": "", - "@link": {}, - "numberedList": "", - "@numberedList": {}, - "quote": "", - "@quote": {}, - "strikethrough": "", - "@strikethrough": {}, - "text": "", - "@text": {}, - "underline": "", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_zh_CN.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_zh_CN.arb deleted file mode 100644 index cc2c79985c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_zh_CN.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "zh-CN", - "bold": "", - "@bold": {}, - "bulletedList": "", - "@bulletedList": {}, - "checkbox": "", - "@checkbox": {}, - "embedCode": "", - "@embedCode": {}, - "heading1": "", - "@heading1": {}, - "heading2": "", - "@heading2": {}, - "heading3": "", - "@heading3": {}, - "highlight": "", - "@highlight": {}, - "image": "", - "@image": {}, - "italic": "", - "@italic": {}, - "link": "", - "@link": {}, - "numberedList": "", - "@numberedList": {}, - "quote": "", - "@quote": {}, - "strikethrough": "", - "@strikethrough": {}, - "text": "", - "@text": {}, - "underline": "", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_zh_TW.arb b/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_zh_TW.arb deleted file mode 100644 index 4d2778a608..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/l10n/intl_zh_TW.arb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@@locale": "zh-TW", - "bold": "", - "@bold": {}, - "bulletedList": "", - "@bulletedList": {}, - "checkbox": "", - "@checkbox": {}, - "embedCode": "", - "@embedCode": {}, - "heading1": "", - "@heading1": {}, - "heading2": "", - "@heading2": {}, - "heading3": "", - "@heading3": {}, - "highlight": "", - "@highlight": {}, - "image": "", - "@image": {}, - "italic": "", - "@italic": {}, - "link": "", - "@link": {}, - "numberedList": "", - "@numberedList": {}, - "quote": "", - "@quote": {}, - "strikethrough": "", - "@strikethrough": {}, - "text": "", - "@text": {}, - "underline": "", - "@underline": {} -} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/attributes_commands.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/attributes_commands.dart deleted file mode 100644 index b506bd100c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/attributes_commands.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:appflowy_editor/src/commands/command_extension.dart'; -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; - -extension TextCommands on EditorState { - Future updateNodeAttributes( - Attributes attributes, { - Path? path, - Node? node, - }) { - return futureCommand(() { - final n = getNode(path: path, node: node); - apply( - transaction..updateNode(n, attributes), - ); - }); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/command_extension.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/command_extension.dart deleted file mode 100644 index 71c3ef4a9d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/command_extension.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'dart:async'; - -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:flutter/widgets.dart'; - -extension CommandExtension on EditorState { - Future futureCommand(void Function() fn) async { - final completer = Completer(); - fn(); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - completer.complete(); - }); - return completer.future; - } - - Node getNode({ - Path? path, - Node? node, - }) { - if (node != null) { - return node; - } else if (path != null) { - return document.nodeAtPath(path)!; - } - throw Exception('path and node cannot be null at the same time'); - } - - TextNode getTextNode({ - Path? path, - TextNode? textNode, - }) { - if (textNode != null) { - return textNode; - } else if (path != null) { - return document.nodeAtPath(path)! as TextNode; - } - throw Exception('path and node cannot be null at the same time'); - } - - Selection getSelection( - Selection? selection, - ) { - final currentSelection = service.selectionService.currentSelection.value; - if (selection != null) { - return selection; - } else if (currentSelection != null) { - return currentSelection; - } - throw Exception('path and textNode cannot be null at the same time'); - } - - List getTextInSelection( - List textNodes, - Selection selection, - ) { - List res = []; - if (selection.isSingle) { - final plainText = textNodes.first.toPlainText(); - res.add(plainText.substring(selection.startIndex, selection.endIndex)); - } else if (!selection.isCollapsed) { - for (var i = 0; i < textNodes.length; i++) { - final plainText = textNodes[i].toPlainText(); - if (i == 0) { - res.add( - plainText.substring( - selection.startIndex, - plainText.length, - ), - ); - } else if (i == textNodes.length - 1) { - res.add(plainText.substring(0, selection.endIndex)); - } else { - res.add(plainText); - } - } - } - return res; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/text/text_commands.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/text/text_commands.dart deleted file mode 100644 index 3a6c62aa62..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/text/text_commands.dart +++ /dev/null @@ -1,127 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; - -extension TextCommands on EditorState { - /// Insert text at the given index of the given [TextNode] or the [Path]. - /// - /// [Path] and [TextNode] are mutually exclusive. - /// One of these two parameters must have a value. - Future insertText( - int index, - String text, { - Path? path, - TextNode? textNode, - }) async { - final n = getTextNode(path: path, textNode: textNode); - return apply( - transaction..insertText(n, index, text), - ); - } - - Future insertTextAtCurrentSelection(String text) async { - final selection = getSelection(null); - assert(selection.isCollapsed); - final textNode = getTextNode(path: selection.start.path); - return insertText( - selection.startIndex, - text, - textNode: textNode, - ); - } - - Future formatText( - EditorState editorState, - Selection? selection, - Attributes attributes, { - Path? path, - TextNode? textNode, - }) async { - final n = getTextNode(path: path, textNode: textNode); - final s = getSelection(selection); - return apply( - transaction..formatText(n, s.startIndex, s.length, attributes), - ); - } - - Future formatTextWithBuiltInAttribute( - EditorState editorState, - String key, - Attributes attributes, { - Selection? selection, - Path? path, - TextNode? textNode, - }) async { - final n = getTextNode(path: path, textNode: textNode); - if (BuiltInAttributeKey.globalStyleKeys.contains(key)) { - final attr = n.attributes - ..removeWhere( - (key, _) => BuiltInAttributeKey.globalStyleKeys.contains(key)) - ..addAll(attributes) - ..addAll({ - BuiltInAttributeKey.subtype: key, - }); - return apply( - transaction..updateNode(n, attr), - ); - } else if (BuiltInAttributeKey.partialStyleKeys.contains(key)) { - final s = getSelection(selection); - return apply( - transaction..formatText(n, s.startIndex, s.length, attributes), - ); - } - } - - Future formatTextToCheckbox( - EditorState editorState, - bool check, { - Path? path, - TextNode? textNode, - }) async { - return formatTextWithBuiltInAttribute( - editorState, - BuiltInAttributeKey.checkbox, - {BuiltInAttributeKey.checkbox: check}, - path: path, - textNode: textNode, - ); - } - - Future formatLinkInText( - EditorState editorState, - String? link, { - Path? path, - TextNode? textNode, - }) async { - return formatTextWithBuiltInAttribute( - editorState, - BuiltInAttributeKey.href, - {BuiltInAttributeKey.href: link}, - path: path, - textNode: textNode, - ); - } - - Future insertNewLine({ - Path? path, - }) async { - final p = path ?? getSelection(null).start.path.next; - final transaction = this.transaction; - transaction.insertNode(p, TextNode.empty()); - transaction.afterSelection = Selection.single( - path: p, - startOffset: 0, - ); - return apply(transaction); - } - - Future insertNewLineAtCurrentSelection() async { - final selection = getSelection(null); - assert(selection.isCollapsed); - final textNode = getTextNode(path: selection.start.path); - final transaction = this.transaction; - transaction.splitText( - textNode, - selection.startIndex, - ); - return apply(transaction); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/attributes.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/attributes.dart deleted file mode 100644 index 163e8e9ab6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/attributes.dart +++ /dev/null @@ -1,51 +0,0 @@ -/// Attributes is used to describe the Node's information. -/// -/// Please note: The keywords in [BuiltInAttributeKey] are reserved. -typedef Attributes = Map; - -Attributes? composeAttributes( - Attributes? base, - Attributes? other, { - keepNull = false, -}) { - base ??= {}; - other ??= {}; - Attributes attributes = { - ...base, - ...other, - }; - - if (!keepNull) { - attributes = Attributes.from(attributes) - ..removeWhere((_, value) => value == null); - } - - return attributes.isNotEmpty ? attributes : null; -} - -Attributes invertAttributes(Attributes? from, Attributes? to) { - from ??= {}; - to ??= {}; - final attributes = Attributes.from({}); - - // key in from but not in to, or value is different - for (final entry in from.entries) { - if ((!to.containsKey(entry.key) && entry.value != null) || - to[entry.key] != entry.value) { - attributes[entry.key] = entry.value; - } - } - - // key in to but not in from, or value is different - for (final entry in to.entries) { - if (!from.containsKey(entry.key) && entry.value != null) { - attributes[entry.key] = null; - } - } - - return attributes; -} - -int hashAttributes(Attributes base) => Object.hashAllUnordered( - base.entries.map((e) => Object.hash(e.key, e.value)), - ); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/document.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/document.dart deleted file mode 100644 index 2994896b58..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/document.dart +++ /dev/null @@ -1,136 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; - -/// [Document] reprensents a AppFlowy Editor document structure. -/// -/// It stores the root of the document. -/// -/// DO NOT directly mutate the properties of a [Document] object. -class Document { - Document({ - required this.root, - }); - - factory Document.fromJson(Map json) { - assert(json['document'] is Map); - - final document = Map.from(json['document'] as Map); - final root = Node.fromJson(document); - return Document(root: root); - } - - /// Creates a empty document with a single text node. - factory Document.empty() { - final root = Node( - type: 'editor', - children: LinkedList()..add(TextNode.empty()), - ); - return Document( - root: root, - ); - } - - final Node root; - - /// Returns the node at the given [path]. - Node? nodeAtPath(Path path) { - return root.childAtPath(path); - } - - /// Inserts a [Node]s at the given [Path]. - bool insert(Path path, Iterable nodes) { - if (path.isEmpty || nodes.isEmpty) { - return false; - } - - final target = nodeAtPath(path); - if (target != null) { - for (final node in nodes) { - target.insertBefore(node); - } - return true; - } - - final parent = nodeAtPath(path.parent); - if (parent != null) { - for (var i = 0; i < nodes.length; i++) { - parent.insert(nodes.elementAt(i), index: path.last + i); - } - return true; - } - - return false; - } - - /// Deletes the [Node]s at the given [Path]. - bool delete(Path path, [int length = 1]) { - if (path.isEmpty || length <= 0) { - return false; - } - var target = nodeAtPath(path); - if (target == null) { - return false; - } - while (target != null && length > 0) { - final next = target.next; - target.unlink(); - target = next; - length--; - } - return true; - } - - /// Updates the [Node] at the given [Path] - bool update(Path path, Attributes attributes) { - if (path.isEmpty) { - return false; - } - final target = nodeAtPath(path); - if (target == null) { - return false; - } - target.updateAttributes(attributes); - return true; - } - - /// Updates the [TextNode] at the given [Path] - bool updateText(Path path, Delta delta) { - if (path.isEmpty) { - return false; - } - final target = nodeAtPath(path); - if (target == null || target is! TextNode) { - return false; - } - target.delta = target.delta.compose(delta); - return true; - } - - bool get isEmpty { - if (root.children.isEmpty) { - return true; - } - - if (root.children.length > 1) { - return false; - } - - final node = root.children.first; - if (node is TextNode && - (node.delta.isEmpty || node.delta.toPlainText().isEmpty)) { - return true; - } - - return false; - } - - Map toJson() { - return { - 'document': root.toJson(), - }; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/node.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/node.dart deleted file mode 100644 index 1bb26da7fb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/node.dart +++ /dev/null @@ -1,301 +0,0 @@ -import 'dart:collection'; - -import 'package:flutter/material.dart'; - -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; - -class Node extends ChangeNotifier with LinkedListEntry { - Node({ - required this.type, - Attributes? attributes, - this.parent, - LinkedList? children, - }) : children = children ?? LinkedList(), - _attributes = attributes ?? {} { - for (final child in this.children) { - child.parent = this; - } - } - - factory Node.fromJson(Map json) { - assert(json['type'] is String); - - final jType = json['type'] as String; - final jChildren = json['children'] as List?; - final jAttributes = json['attributes'] != null - ? Attributes.from(json['attributes'] as Map) - : Attributes.from({}); - - final children = LinkedList(); - if (jChildren != null) { - children.addAll( - jChildren.map( - (jChild) => Node.fromJson( - Map.from(jChild), - ), - ), - ); - } - - Node node; - - if (jType == 'text') { - final jDelta = json['delta'] as List?; - final delta = jDelta == null ? Delta() : Delta.fromJson(jDelta); - node = TextNode( - children: children, - attributes: jAttributes, - delta: delta, - ); - } else { - node = Node( - type: jType, - children: children, - attributes: jAttributes, - ); - } - - for (final child in children) { - child.parent = node; - } - - return node; - } - - final String type; - final LinkedList children; - Node? parent; - Attributes _attributes; - - // Renderable - final key = GlobalKey(); - final layerLink = LayerLink(); - - Attributes get attributes => {..._attributes}; - - String get id { - if (subtype != null) { - return '$type/$subtype'; - } - return type; - } - - String? get subtype { - if (attributes[BuiltInAttributeKey.subtype] is String) { - return attributes[BuiltInAttributeKey.subtype] as String; - } - return null; - } - - Path get path => _computePath(); - - void updateAttributes(Attributes attributes) { - final oldAttributes = this.attributes; - - _attributes = composeAttributes(this.attributes, attributes) ?? {}; - - // Notifies the new attributes - // if attributes contains 'subtype', should notify parent to rebuild node - // else, just notify current node. - bool shouldNotifyParent = - this.attributes['subtype'] != oldAttributes['subtype']; - shouldNotifyParent ? parent?.notifyListeners() : notifyListeners(); - } - - Node? childAtIndex(int index) { - if (children.length <= index || index < 0) { - return null; - } - - return children.elementAt(index); - } - - Node? childAtPath(Path path) { - if (path.isEmpty) { - return this; - } - - return childAtIndex(path.first)?.childAtPath(path.sublist(1)); - } - - void insert(Node entry, {int? index}) { - final length = children.length; - index ??= length; - - if (children.isEmpty) { - entry.parent = this; - children.add(entry); - notifyListeners(); - return; - } - - // If index is out of range, insert at the end. - // If index is negative, insert at the beginning. - // If index is positive, insert at the index. - if (index >= length) { - children.last.insertAfter(entry); - } else if (index <= 0) { - children.first.insertBefore(entry); - } else { - childAtIndex(index)?.insertBefore(entry); - } - } - - @override - void insertAfter(Node entry) { - entry.parent = parent; - super.insertAfter(entry); - - // Notifies the new node. - parent?.notifyListeners(); - } - - @override - void insertBefore(Node entry) { - entry.parent = parent; - super.insertBefore(entry); - - // Notifies the new node. - parent?.notifyListeners(); - } - - @override - void unlink() { - super.unlink(); - - parent?.notifyListeners(); - parent = null; - } - - Map toJson() { - var map = { - 'type': type, - }; - if (children.isNotEmpty) { - map['children'] = - children.map((node) => node.toJson()).toList(growable: false); - } - if (attributes.isNotEmpty) { - map['attributes'] = attributes; - } - return map; - } - - Node copyWith({ - String? type, - LinkedList? children, - Attributes? attributes, - }) { - final node = Node( - type: type ?? this.type, - attributes: attributes ?? {...this.attributes}, - children: children, - ); - if (children == null && this.children.isNotEmpty) { - for (final child in this.children) { - node.children.add( - child.copyWith()..parent = node, - ); - } - } - return node; - } - - Path _computePath([Path previous = const []]) { - if (parent == null) { - return previous; - } - var index = 0; - for (final child in parent!.children) { - if (child == this) { - break; - } - index += 1; - } - return parent!._computePath([index, ...previous]); - } -} - -class TextNode extends Node { - TextNode({ - required Delta delta, - LinkedList? children, - Attributes? attributes, - }) : _delta = delta, - super( - type: 'text', - children: children, - attributes: attributes ?? {}, - ); - - TextNode.empty({Attributes? attributes}) - : _delta = Delta(operations: [TextInsert('')]), - super( - type: 'text', - attributes: attributes ?? {}, - ); - - Delta _delta; - Delta get delta => _delta; - set delta(Delta v) { - _delta = v; - notifyListeners(); - } - - @override - Map toJson() { - final map = super.toJson(); - map['delta'] = delta.toJson(); - return map; - } - - @override - TextNode copyWith({ - String? type = 'text', - LinkedList? children, - Attributes? attributes, - Delta? delta, - }) { - final textNode = TextNode( - children: children, - attributes: attributes ?? this.attributes, - delta: delta ?? this.delta, - ); - if (children == null && this.children.isNotEmpty) { - for (final child in this.children) { - textNode.children.add( - child.copyWith()..parent = textNode, - ); - } - } - return textNode; - } - - String toPlainText() => _delta.toPlainText(); -} - -extension NodeEquality on Iterable { - bool equals(Iterable other) { - if (length != other.length) { - return false; - } - for (var i = 0; i < length; i++) { - if (!_nodeEquals(elementAt(i), other.elementAt(i))) { - return false; - } - } - return true; - } - - bool _nodeEquals(T base, U other) { - if (identical(this, other)) return true; - - return base is Node && - other is Node && - other.type == base.type && - other.children.equals(base.children); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/node_iterator.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/node_iterator.dart deleted file mode 100644 index ef086af8cb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/node_iterator.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/document.dart'; - -/// [NodeIterator] is used to traverse the nodes in visual order. -class NodeIterator implements Iterator { - NodeIterator({ - required this.document, - required this.startNode, - this.endNode, - }); - - final Document document; - final Node startNode; - final Node? endNode; - - Node? _currentNode; - bool _began = false; - - @override - Node get current => _currentNode!; - - @override - bool moveNext() { - if (!_began) { - _currentNode = startNode; - _began = true; - return true; - } - - if (_currentNode == null) { - return false; - } - Node node = _currentNode!; - - if (endNode != null && endNode == node) { - _currentNode = null; - return false; - } - - if (node.children.isNotEmpty) { - _currentNode = node.children.first; - } else if (node.next != null) { - _currentNode = node.next!; - } else { - while (node.parent != null) { - node = node.parent!; - final nextOfParent = node.next; - if (nextOfParent == null) { - _currentNode = null; - } else { - _currentNode = nextOfParent; - break; - } - } - } - - return _currentNode != null; - } - - List toList() { - final result = []; - while (moveNext()) { - result.add(current); - } - return result; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/path.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/path.dart deleted file mode 100644 index 5e411407d5..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/path.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/foundation.dart'; - -typedef Path = List; - -extension PathExtensions on Path { - bool equals(Path other) { - return listEquals(this, other); - } - - bool operator >=(Path other) { - if (equals(other)) { - return true; - } - return this > other; - } - - bool operator >(Path other) { - if (equals(other)) { - return false; - } - final length = min(this.length, other.length); - for (var i = 0; i < length; i++) { - if (this[i] < other[i]) { - return false; - } else if (this[i] > other[i]) { - return true; - } - } - if (this.length < other.length) { - return false; - } - return true; - } - - bool operator <=(Path other) { - if (equals(other)) { - return true; - } - return this < other; - } - - bool operator <(Path other) { - if (equals(other)) { - return false; - } - final length = min(this.length, other.length); - for (var i = 0; i < length; i++) { - if (this[i] > other[i]) { - return false; - } else if (this[i] < other[i]) { - return true; - } - } - if (this.length > other.length) { - return false; - } - return true; - } - - Path get next { - Path nextPath = Path.from(this, growable: true); - if (isEmpty) { - return nextPath; - } - final last = nextPath.last; - return nextPath - ..removeLast() - ..add(last + 1); - } - - Path get previous { - Path previousPath = Path.from(this, growable: true); - if (isEmpty) { - return previousPath; - } - final last = previousPath.last; - return previousPath - ..removeLast() - ..add(max(0, last - 1)); - } - - Path get parent { - if (isEmpty) { - return this; - } - return Path.from(this, growable: true)..removeLast(); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/text_delta.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/text_delta.dart deleted file mode 100644 index 9aa66d962a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/document/text_delta.dart +++ /dev/null @@ -1,548 +0,0 @@ -import 'dart:collection'; -import 'dart:math'; - -import 'package:flutter/foundation.dart'; - -import 'package:appflowy_editor/src/core/document/attributes.dart'; - -// constant number: 2^53 - 1 -const int _maxInt = 9007199254740991; - -List stringIndexes(String text) { - final indexes = List.filled(text.length, 0); - final iterator = text.runes.iterator; - - while (iterator.moveNext()) { - for (var i = 0; i < iterator.currentSize; i++) { - indexes[iterator.rawIndex + i] = iterator.rawIndex; - } - } - - return indexes; -} - -abstract class TextOperation { - Attributes? get attributes; - int get length; - - bool get isEmpty => length == 0; - - Map toJson(); -} - -class TextInsert extends TextOperation { - TextInsert( - this.text, { - Attributes? attributes, - }) : _attributes = attributes; - - String text; - final Attributes? _attributes; - - @override - int get length => text.length; - - @override - Attributes? get attributes => _attributes != null ? {..._attributes!} : null; - - @override - Map toJson() { - final result = { - 'insert': text, - }; - if (_attributes != null && _attributes!.isNotEmpty) { - result['attributes'] = attributes; - } - return result; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is TextInsert && - other.text == text && - _mapEquals(_attributes, other._attributes); - } - - @override - int get hashCode => text.hashCode ^ _attributes.hashCode; -} - -class TextRetain extends TextOperation { - TextRetain( - this.length, { - Attributes? attributes, - }) : _attributes = attributes; - - @override - int length; - final Attributes? _attributes; - - @override - Attributes? get attributes => _attributes != null ? {..._attributes!} : null; - - @override - Map toJson() { - final result = { - 'retain': length, - }; - if (_attributes != null && _attributes!.isNotEmpty) { - result['attributes'] = attributes; - } - return result; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is TextRetain && - other.length == length && - _mapEquals(_attributes, other._attributes); - } - - @override - int get hashCode => length.hashCode ^ _attributes.hashCode; -} - -class TextDelete extends TextOperation { - TextDelete({ - required this.length, - }); - - @override - int length; - - @override - Attributes? get attributes => null; - - @override - Map toJson() { - return { - 'delete': length, - }; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is TextDelete && other.length == length; - } - - @override - int get hashCode => length.hashCode; -} - -/// Deltas are a simple, yet expressive format that can be used to describe contents and changes. -/// The format is JSON based, and is human readable, yet easily parsible by machines. -/// Deltas can describe any rich text document, includes all text and formatting information, without the ambiguity and complexity of HTML. -/// - -/// Basically borrowed from: https://github.com/quilljs/delta -class Delta extends Iterable { - Delta({ - List? operations, - }) : _operations = operations ?? []; - - factory Delta.fromJson(List list) { - final operations = []; - - for (final value in list) { - if (value is Map) { - final op = _textOperationFromJson(value); - if (op != null) { - operations.add(op); - } - } - } - - return Delta(operations: operations); - } - - final List _operations; - String? _plainText; - List? _runeIndexes; - - void addAll(Iterable textOperations) { - textOperations.forEach(add); - } - - void add(TextOperation textOperation) { - if (textOperation.isEmpty) { - return; - } - _plainText = null; - - if (_operations.isNotEmpty) { - final lastOp = _operations.last; - if (lastOp is TextDelete && textOperation is TextDelete) { - lastOp.length += textOperation.length; - return; - } - if (_mapEquals(lastOp.attributes, textOperation.attributes)) { - if (lastOp is TextInsert && textOperation is TextInsert) { - lastOp.text += textOperation.text; - return; - } - // if there is an delete before the insert - // swap the order - if (lastOp is TextDelete && textOperation is TextInsert) { - _operations.removeLast(); - _operations.add(textOperation); - _operations.add(lastOp); - return; - } - if (lastOp is TextRetain && textOperation is TextRetain) { - lastOp.length += textOperation.length; - return; - } - } - } - - _operations.add(textOperation); - } - - /// The slice() method does not change the original string. - /// The start and end parameters specifies the part of the string to extract. - /// The end position is optional. - Delta slice(int start, [int? end]) { - final result = Delta(); - final iterator = _OpIterator(_operations); - int index = 0; - - while ((end == null || index < end) && iterator.hasNext) { - TextOperation? nextOp; - if (index < start) { - nextOp = iterator._next(start - index); - } else { - nextOp = iterator._next(end == null ? null : end - index); - result.add(nextOp); - } - - index += nextOp.length; - } - - return result; - } - - /// Insert operations have an `insert` key defined. - /// A String value represents inserting text. - void insert(String text, {Attributes? attributes}) => - add(TextInsert(text, attributes: attributes)); - - /// Retain operations have a Number `retain` key defined representing the number of characters to keep (other libraries might use the name keep or skip). - /// An optional `attributes` key can be defined with an Object to describe formatting changes to the character range. - /// A value of `null` in the `attributes` Object represents removal of that key. - /// - /// *Note: It is not necessary to retain the last characters of a document as this is implied.* - void retain(int length, {Attributes? attributes}) => - add(TextRetain(length, attributes: attributes)); - - /// Delete operations have a Number `delete` key defined representing the number of characters to delete. - void delete(int length) => add(TextDelete(length: length)); - - /// The length of the string fo the [Delta]. - @override - int get length { - return _operations.fold( - 0, (previousValue, element) => previousValue + element.length); - } - - /// Returns a Delta that is equivalent to applying the operations of own Delta, followed by another Delta. - Delta compose(Delta other) { - final thisIter = _OpIterator(_operations); - final otherIter = _OpIterator(other._operations); - final operations = []; - - final firstOther = otherIter.peek(); - if (firstOther != null && - firstOther is TextRetain && - firstOther.attributes == null) { - int firstLeft = firstOther.length; - while ( - thisIter.peek() is TextInsert && thisIter.peekLength() <= firstLeft) { - firstLeft -= thisIter.peekLength(); - final next = thisIter._next(); - operations.add(next); - } - if (firstOther.length - firstLeft > 0) { - otherIter._next(firstOther.length - firstLeft); - } - } - - final delta = Delta(operations: operations); - while (thisIter.hasNext || otherIter.hasNext) { - if (otherIter.peek() is TextInsert) { - final next = otherIter._next(); - delta.add(next); - } else if (thisIter.peek() is TextDelete) { - final next = thisIter._next(); - delta.add(next); - } else { - // otherIs - final length = min(thisIter.peekLength(), otherIter.peekLength()); - final thisOp = thisIter._next(length); - final otherOp = otherIter._next(length); - final attributes = composeAttributes( - thisOp.attributes, - otherOp.attributes, - keepNull: thisOp is TextRetain, - ); - - if (otherOp is TextRetain && otherOp.length > 0) { - TextOperation? newOp; - if (thisOp is TextRetain) { - newOp = TextRetain(length, attributes: attributes); - } else if (thisOp is TextInsert) { - newOp = TextInsert(thisOp.text, attributes: attributes); - } - - if (newOp != null) { - delta.add(newOp); - } - - // Optimization if rest of other is just retain - if (!otherIter.hasNext && - delta._operations.isNotEmpty && - delta._operations.last == newOp) { - final rest = Delta(operations: thisIter.rest()); - return (delta + rest)..chop(); - } - } else if (otherOp is TextDelete && (thisOp is TextRetain)) { - delta.add(otherOp); - } - } - } - - return delta..chop(); - } - - /// This method joins two Delta together. - Delta operator +(Delta other) { - var operations = [..._operations]; - if (other._operations.isNotEmpty) { - operations.add(other._operations[0]); - operations.addAll(other._operations.sublist(1)); - } - return Delta(operations: operations); - } - - void chop() { - if (_operations.isEmpty) { - return; - } - _plainText = null; - final lastOp = _operations.last; - if (lastOp is TextRetain && (lastOp.attributes?.length ?? 0) == 0) { - _operations.removeLast(); - } - } - - @override - bool operator ==(Object other) { - if (other is! Delta) { - return false; - } - return listEquals(_operations, other._operations); - } - - @override - int get hashCode { - return Object.hashAll(_operations); - } - - /// Returned an inverted delta that has the opposite effect of against a base document delta. - Delta invert(Delta base) { - final inverted = Delta(); - _operations.fold(0, (int previousValue, op) { - if (op is TextInsert) { - inverted.delete(op.length); - } else if (op is TextRetain && op.attributes == null) { - inverted.retain(op.length); - return previousValue + op.length; - } else if (op is TextDelete || op is TextRetain) { - final length = op.length; - final slice = base.slice(previousValue, previousValue + length); - for (final baseOp in slice._operations) { - if (op is TextDelete) { - inverted.add(baseOp); - } else if (op is TextRetain && op.attributes != null) { - inverted.retain( - baseOp.length, - attributes: invertAttributes(baseOp.attributes, op.attributes), - ); - } - } - return previousValue + length; - } - return previousValue; - }); - return inverted..chop(); - } - - List toJson() { - return _operations.map((e) => e.toJson()).toList(); - } - - /// This method will return the position of the previous rune. - /// - /// Since the encoding of the [String] in Dart is UTF-16. - /// If you want to find the previous character of a position, - /// you can' just use the `position - 1` simply. - /// - /// This method can help you to compute the position of the previous character. - int prevRunePosition(int pos) { - if (pos == 0) { - return pos - 1; - } - _plainText ??= - _operations.whereType().map((op) => op.text).join(); - _runeIndexes ??= stringIndexes(_plainText!); - return _runeIndexes![pos - 1]; - } - - /// This method will return the position of the next rune. - /// - /// Since the encoding of the [String] in Dart is UTF-16. - /// If you want to find the previous character of a position, - /// you can' just use the `position + 1` simply. - /// - /// This method can help you to compute the position of the next character. - int nextRunePosition(int pos) { - final stringContent = toPlainText(); - if (pos >= stringContent.length - 1) { - return stringContent.length; - } - _runeIndexes ??= stringIndexes(_plainText!); - - for (var i = pos + 1; i < _runeIndexes!.length; i++) { - if (_runeIndexes![i] != pos) { - return _runeIndexes![i]; - } - } - - return stringContent.length; - } - - String toPlainText() { - _plainText ??= - _operations.whereType().map((op) => op.text).join(); - return _plainText!; - } - - @override - Iterator get iterator => _operations.iterator; - - static TextOperation? _textOperationFromJson(Map json) { - TextOperation? operation; - - if (json['insert'] is String) { - final attributes = json['attributes'] as Map?; - operation = TextInsert( - json['insert'] as String, - attributes: attributes != null ? {...attributes} : null, - ); - } else if (json['retain'] is int) { - final attrs = json['attributes'] as Map?; - operation = TextRetain( - json['retain'] as int, - attributes: attrs != null ? {...attrs} : null, - ); - } else if (json['delete'] is int) { - operation = TextDelete(length: json['delete'] as int); - } - - return operation; - } -} - -class _OpIterator { - _OpIterator( - Iterable operations, - ) : _operations = UnmodifiableListView(operations); - - final UnmodifiableListView _operations; - int _index = 0; - int _offset = 0; - - bool get hasNext { - return peekLength() < _maxInt; - } - - TextOperation? peek() { - if (_index >= _operations.length) { - return null; - } - - return _operations[_index]; - } - - int peekLength() { - if (_index < _operations.length) { - final op = _operations[_index]; - return op.length - _offset; - } - return _maxInt; - } - - TextOperation _next([int? length]) { - length ??= _maxInt; - - if (_index >= _operations.length) { - return TextRetain(_maxInt); - } - - final nextOp = _operations[_index]; - - final offset = _offset; - final opLength = nextOp.length; - if (length >= opLength - offset) { - length = opLength - offset; - _index += 1; - _offset = 0; - } else { - _offset += length; - } - if (nextOp is TextDelete) { - return TextDelete(length: length); - } - - if (nextOp is TextRetain) { - return TextRetain(length, attributes: nextOp.attributes); - } - - if (nextOp is TextInsert) { - return TextInsert( - nextOp.text.substring(offset, offset + length), - attributes: nextOp.attributes, - ); - } - - return TextRetain(_maxInt); - } - - List rest() { - if (!hasNext) { - return []; - } else if (_offset == 0) { - return _operations.sublist(_index); - } else { - final offset = _offset; - final index = _index; - final next = _next(); - final rest = _operations.sublist(_index); - _offset = offset; - _index = index; - return [next] + rest; - } - } -} - -bool _mapEquals(Map? a, Map? b) { - if ((a == null || a.isEmpty) && (b == null || b.isEmpty)) { - return true; - } - return mapEquals(a, b); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/legacy/built_in_attribute_keys.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/legacy/built_in_attribute_keys.dart deleted file mode 100644 index 6421e7efd0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/legacy/built_in_attribute_keys.dart +++ /dev/null @@ -1,60 +0,0 @@ -/// -/// Supported partial rendering types: -/// bold, italic, -/// underline, strikethrough, -/// color, font, -/// href -/// -/// Supported global rendering types: -/// heading: h1, h2, h3, h4, h5, h6, ... -/// block quote, -/// list: ordered list, bulleted list, -/// code block -/// -class BuiltInAttributeKey { - static String bold = 'bold'; - static String italic = 'italic'; - static String underline = 'underline'; - static String strikethrough = 'strikethrough'; - static String color = 'color'; - static String backgroundColor = 'backgroundColor'; - static String font = 'font'; - static String href = 'href'; - - static String subtype = 'subtype'; - static String heading = 'heading'; - static String h1 = 'h1'; - static String h2 = 'h2'; - static String h3 = 'h3'; - static String h4 = 'h4'; - static String h5 = 'h5'; - static String h6 = 'h6'; - - static String bulletedList = 'bulleted-list'; - static String numberList = 'number-list'; - - static String quote = 'quote'; - static String checkbox = 'checkbox'; - static String code = 'code'; - static String number = 'number'; - - static List partialStyleKeys = [ - BuiltInAttributeKey.bold, - BuiltInAttributeKey.italic, - BuiltInAttributeKey.underline, - BuiltInAttributeKey.strikethrough, - BuiltInAttributeKey.backgroundColor, - BuiltInAttributeKey.color, - BuiltInAttributeKey.href, - BuiltInAttributeKey.code, - ]; - - static List globalStyleKeys = [ - BuiltInAttributeKey.subtype, - BuiltInAttributeKey.heading, - BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.bulletedList, - BuiltInAttributeKey.numberList, - BuiltInAttributeKey.quote, - ]; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/location/position.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/location/position.dart deleted file mode 100644 index 4f3d104d2d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/location/position.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:appflowy_editor/src/core/document/path.dart'; - -class Position { - final Path path; - final int offset; - - Position({ - required this.path, - this.offset = 0, - }); - - factory Position.fromJson(Map json) { - final path = Path.from(json['path'] as List); - final offset = json['offset']; - return Position( - path: path, - offset: offset ?? 0, - ); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Position && - other.path.equals(path) && - other.offset == offset; - } - - @override - int get hashCode => Object.hash(offset, Object.hashAll(path)); - - @override - String toString() => 'path = $path, offset = $offset'; - - Position copyWith({Path? path, int? offset}) { - return Position( - path: path ?? this.path, - offset: offset ?? this.offset, - ); - } - - Map toJson() { - return { - 'path': path, - 'offset': offset, - }; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/location/selection.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/location/selection.dart deleted file mode 100644 index 39410897eb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/location/selection.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; - -/// Selection represents the selected area or the cursor area in the editor. -/// -/// [Selection] is directional. -/// -/// 1. forward,the end position is before the start position. -/// 2. backward, the end position is after the start position. -/// 3. collapsed, the end position is equal to the start position. -class Selection { - /// Create a selection with [start], [end]. - Selection({ - required this.start, - required this.end, - }); - - factory Selection.fromJson(Map json) { - return Selection( - start: Position.fromJson(json['start']), - end: Position.fromJson(json['end']), - ); - } - - /// Create a selection with [Path], [startOffset] and [endOffset]. - /// - /// The [endOffset] is optional. - /// - /// This constructor will return a collapsed [Selection] if [endOffset] is null. - /// - Selection.single({ - required Path path, - required int startOffset, - int? endOffset, - }) : start = Position(path: path, offset: startOffset), - end = Position(path: path, offset: endOffset ?? startOffset); - - /// Create a collapsed selection with [position]. - Selection.collapsed(Position position) - : start = position, - end = position; - - final Position start; - final Position end; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Selection && other.start == start && other.end == end; - } - - @override - int get hashCode => start.hashCode ^ end.hashCode; - - @override - String toString() => 'start = $start, end = $end'; - - /// Returns a Boolean indicating whether the selection's start and end points - /// are at the same position. - bool get isCollapsed => start == end; - - /// Returns a Boolean indicating whether the selection's start and end points - /// are at the same path. - bool get isSingle => start.path.equals(end.path); - - /// Returns a Boolean indicating whether the selection is forward. - bool get isForward => - (start.path > end.path) || (isSingle && start.offset > end.offset); - - /// Returns a Boolean indicating whether the selection is backward. - bool get isBackward => - (start.path < end.path) || (isSingle && start.offset < end.offset); - - /// Returns a normalized selection that direction is forward. - Selection get normalized => isBackward ? copyWith() : reversed.copyWith(); - - /// Returns a reversed selection. - Selection get reversed => copyWith(start: end, end: start); - - /// Returns the offset in the starting position under the normalized selection. - int get startIndex => normalized.start.offset; - - /// Returns the offset in the ending position under the normalized selection. - int get endIndex => normalized.end.offset; - - int get length => endIndex - startIndex; - - /// Collapses the current selection to a single point. - /// - /// If [atStart] is true, the selection will be collapsed to the start point. - /// If [atStart] is false, the selection will be collapsed to the end point. - Selection collapse({bool atStart = false}) { - if (atStart) { - return copyWith(end: start); - } else { - return copyWith(start: end); - } - } - - Selection copyWith({Position? start, Position? end}) { - return Selection( - start: start ?? this.start, - end: end ?? this.end, - ); - } - - Map toJson() { - return { - 'start': start.toJson(), - 'end': end.toJson(), - }; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/operation.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/operation.dart deleted file mode 100644 index 31662a61ca..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/operation.dart +++ /dev/null @@ -1,273 +0,0 @@ -import 'package:flutter/foundation.dart'; - -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; - -/// [Operation] represents a change to a [Document]. -abstract class Operation { - Operation( - this.path, - ); - - factory Operation.fromJson() => throw UnimplementedError(); - - final Path path; - - /// Inverts the operation. - /// - /// Returns the inverted operation. - Operation invert(); - - /// Returns the JSON representation of the operation. - Map toJson(); - - Operation copyWith({Path? path}); -} - -/// [InsertOperation] represents an insert operation. -class InsertOperation extends Operation { - InsertOperation( - super.path, - this.nodes, - ); - - factory InsertOperation.fromJson(Map json) { - final path = json['path'] as Path; - final nodes = (json['nodes'] as List) - .map((n) => Node.fromJson(n)) - .toList(growable: false); - return InsertOperation(path, nodes); - } - - final Iterable nodes; - - @override - Operation invert() => DeleteOperation(path, nodes); - - @override - Map toJson() { - return { - 'op': 'insert', - 'path': path, - 'nodes': nodes.map((n) => n.toJson()).toList(growable: false), - }; - } - - @override - Operation copyWith({Path? path}) { - return InsertOperation(path ?? this.path, nodes); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is InsertOperation && - other.path.equals(path) && - other.nodes.equals(nodes); - } - - @override - int get hashCode => path.hashCode ^ Object.hashAll(nodes); -} - -/// [DeleteOperation] represents a delete operation. -class DeleteOperation extends Operation { - DeleteOperation( - super.path, - this.nodes, - ); - - factory DeleteOperation.fromJson(Map json) { - final path = json['path'] as Path; - final nodes = (json['nodes'] as List) - .map((n) => Node.fromJson(n)) - .toList(growable: false); - return DeleteOperation(path, nodes); - } - - final Iterable nodes; - - @override - Operation invert() => InsertOperation(path, nodes); - - @override - Map toJson() { - return { - 'op': 'delete', - 'path': path, - 'nodes': nodes.map((n) => n.toJson()).toList(growable: false), - }; - } - - @override - Operation copyWith({Path? path}) { - return DeleteOperation(path ?? this.path, nodes); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is DeleteOperation && - other.path.equals(path) && - other.nodes.equals(nodes); - } - - @override - int get hashCode => path.hashCode ^ Object.hashAll(nodes); -} - -/// [UpdateOperation] represents an attributes update operation. -class UpdateOperation extends Operation { - UpdateOperation( - super.path, - this.attributes, - this.oldAttributes, - ); - - factory UpdateOperation.fromJson(Map json) { - final path = json['path'] as Path; - final oldAttributes = json['oldAttributes'] as Attributes; - final attributes = json['attributes'] as Attributes; - return UpdateOperation( - path, - attributes, - oldAttributes, - ); - } - - final Attributes attributes; - final Attributes oldAttributes; - - @override - Operation invert() => UpdateOperation( - path, - oldAttributes, - attributes, - ); - - @override - Map toJson() { - return { - 'op': 'update', - 'path': path, - 'attributes': {...attributes}, - 'oldAttributes': {...oldAttributes}, - }; - } - - @override - Operation copyWith({Path? path}) { - return UpdateOperation( - path ?? this.path, - {...attributes}, - {...oldAttributes}, - ); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is UpdateOperation && - other.path.equals(path) && - mapEquals(other.attributes, attributes) && - mapEquals(other.oldAttributes, oldAttributes); - } - - @override - int get hashCode => - path.hashCode ^ attributes.hashCode ^ oldAttributes.hashCode; -} - -/// [UpdateTextOperation] represents a text update operation. -class UpdateTextOperation extends Operation { - UpdateTextOperation( - super.path, - this.delta, - this.inverted, - ); - - factory UpdateTextOperation.fromJson(Map json) { - final path = json['path'] as Path; - final delta = Delta.fromJson(json['delta']); - final inverted = Delta.fromJson(json['inverted']); - return UpdateTextOperation(path, delta, inverted); - } - - final Delta delta; - final Delta inverted; - - @override - Operation invert() => UpdateTextOperation(path, inverted, delta); - - @override - Map toJson() { - return { - 'op': 'update_text', - 'path': path, - 'delta': delta.toJson(), - 'inverted': inverted.toJson(), - }; - } - - @override - Operation copyWith({Path? path}) { - return UpdateTextOperation(path ?? this.path, delta, inverted); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is UpdateTextOperation && - other.path.equals(path) && - other.delta == delta && - other.inverted == inverted; - } - - @override - int get hashCode => delta.hashCode ^ inverted.hashCode; -} - -// TODO(Lucas.Xu): refactor this part -Path transformPath(Path preInsertPath, Path b, [int delta = 1]) { - if (preInsertPath.length > b.length) { - return b; - } - if (preInsertPath.isEmpty || b.isEmpty) { - return b; - } - // check the prefix - for (var i = 0; i < preInsertPath.length - 1; i++) { - if (preInsertPath[i] != b[i]) { - return b; - } - } - final prefix = preInsertPath.sublist(0, preInsertPath.length - 1); - final suffix = b.sublist(preInsertPath.length); - final preInsertLast = preInsertPath.last; - final bAtIndex = b[preInsertPath.length - 1]; - if (preInsertLast <= bAtIndex) { - prefix.add(bAtIndex + delta); - } else { - prefix.add(bAtIndex); - } - prefix.addAll(suffix); - return prefix; -} - -Operation transformOperation(Operation a, Operation b) { - if (a is InsertOperation) { - final newPath = transformPath(a.path, b.path, a.nodes.length); - return b.copyWith(path: newPath); - } else if (a is DeleteOperation) { - final newPath = transformPath(a.path, b.path, -1 * a.nodes.length); - return b.copyWith(path: newPath); - } - // TODO: transform update and textedit - return b; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/transaction.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/transaction.dart deleted file mode 100644 index 6c369f566e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/transaction.dart +++ /dev/null @@ -1,444 +0,0 @@ -import 'dart:math'; - -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/document/document.dart'; -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/core/transform/operation.dart'; - -/// A [Transaction] has a list of [Operation] objects that will be applied -/// to the editor. -/// -/// There will be several ways to consume the transaction: -/// 1. Apply to the state to update the UI. -/// 2. Send to the backend to store and do operation transforming. -class Transaction { - Transaction({ - required this.document, - }); - - final Document document; - - /// The operations to be applied. - final List operations = []; - - /// The selection to be applied. - Selection? afterSelection; - - /// The before selection is to be recovered if needed. - Selection? beforeSelection; - - /// Inserts the [Node] at the given [Path]. - void insertNode( - Path path, - Node node, { - bool deepCopy = true, - }) { - insertNodes(path, [node], deepCopy: deepCopy); - } - - /// Inserts a sequence of [Node]s at the given [Path]. - void insertNodes( - Path path, - Iterable nodes, { - bool deepCopy = true, - }) { - if (deepCopy) { - add(InsertOperation(path, nodes.map((e) => e.copyWith()))); - } else { - add(InsertOperation(path, nodes)); - } - } - - /// Updates the attributes of the [Node]. - /// - /// The [attributes] will be merged into the existing attributes. - void updateNode(Node node, Attributes attributes) { - final inverted = invertAttributes(node.attributes, attributes); - add(UpdateOperation( - node.path, - {...attributes}, - inverted, - )); - } - - /// Deletes the [Node] in the document. - void deleteNode(Node node) { - deleteNodesAtPath(node.path); - } - - /// Deletes the [Node]s in the document. - void deleteNodes(Iterable nodes) { - nodes.forEach(deleteNode); - } - - /// Deletes the [Node]s at the given [Path]. - /// - /// The [length] indicates the number of consecutive deletions, - /// including the node of the current path. - void deleteNodesAtPath(Path path, [int length = 1]) { - if (path.isEmpty) return; - final nodes = []; - final parent = path.parent; - for (var i = 0; i < length; i++) { - final node = document.nodeAtPath(parent + [path.last + i]); - if (node == null) { - break; - } - nodes.add(node); - } - add(DeleteOperation(path, nodes)); - } - - /// Update the [TextNode]s with the given [Delta]. - void updateText(TextNode textNode, Delta delta) { - final inverted = delta.invert(textNode.delta); - add(UpdateTextOperation(textNode.path, delta, inverted)); - } - - /// Returns the JSON representation of the transaction. - Map toJson() { - final json = {}; - if (operations.isNotEmpty) { - json['operations'] = operations.map((o) => o.toJson()).toList(); - } - if (afterSelection != null) { - json['after_selection'] = afterSelection!.toJson(); - } - if (beforeSelection != null) { - json['before_selection'] = beforeSelection!.toJson(); - } - return json; - } - - /// Adds an operation to the transaction. - /// This method will merge operations if they are both TextEdits. - /// - /// Also, this method will transform the path of the operations - /// to avoid conflicts. - void add(Operation op, {bool transform = true}) { - final Operation? last = operations.isEmpty ? null : operations.last; - if (last != null) { - if (op is UpdateTextOperation && - last is UpdateTextOperation && - op.path.equals(last.path)) { - final newOp = UpdateTextOperation( - op.path, - last.delta.compose(op.delta), - op.inverted.compose(last.inverted), - ); - operations[operations.length - 1] = newOp; - return; - } - } - if (transform) { - for (var i = 0; i < operations.length; i++) { - op = transformOperation(operations[i], op); - } - } - if (op is UpdateTextOperation && op.delta.isEmpty) { - return; - } - operations.add(op); - } -} - -extension TextTransaction on Transaction { - void mergeText( - TextNode first, - TextNode second, { - int? firstOffset, - int secondOffset = 0, - }) { - final firstLength = first.delta.length; - final secondLength = second.delta.length; - firstOffset ??= firstLength; - updateText( - first, - Delta() - ..retain(firstOffset) - ..delete(firstLength - firstOffset) - ..addAll(second.delta.slice(secondOffset, secondLength)), - ); - afterSelection = Selection.collapsed(Position( - path: first.path, - offset: firstOffset, - )); - } - - void splitText(TextNode textNode, int offset) { - final delta = textNode.delta; - final second = delta.slice(offset, delta.length); - final path = textNode.path.next; - deleteText(textNode, offset, delta.length); - insertNode( - path, - TextNode( - attributes: textNode.attributes, - delta: second, - ), - ); - afterSelection = Selection.collapsed(Position( - path: path, - offset: 0, - )); - } - - /// Inserts the text content at a specified index. - /// - /// Optionally, you may specify formatting attributes that are applied to the inserted string. - /// By default, the formatting attributes before the insert position will be reused. - void insertText( - TextNode textNode, - int index, - String text, { - Attributes? attributes, - }) { - var newAttributes = attributes; - if (index != 0 && attributes == null) { - newAttributes = - textNode.delta.slice(max(index - 1, 0), index).first.attributes; - if (newAttributes != null) { - newAttributes = {...newAttributes}; // make a copy - } - } - updateText( - textNode, - Delta() - ..retain(index) - ..insert(text, attributes: newAttributes), - ); - afterSelection = Selection.collapsed( - Position(path: textNode.path, offset: index + text.length), - ); - } - - /// Assigns a formatting attributes to a range of text. - void formatText( - TextNode textNode, - int index, - int length, - Attributes attributes, - ) { - afterSelection = beforeSelection; - updateText( - textNode, - Delta() - ..retain(index) - ..retain(length, attributes: attributes), - ); - } - - /// Deletes the text of specified length starting at index. - void deleteText( - TextNode textNode, - int index, - int length, - ) { - updateText( - textNode, - Delta() - ..retain(index) - ..delete(length), - ); - afterSelection = Selection.collapsed( - Position(path: textNode.path, offset: index), - ); - } - - /// Replaces the text of specified length starting at index. - /// - /// Optionally, you may specify formatting attributes that are applied to the inserted string. - /// By default, the formatting attributes before the insert position will be reused. - void replaceText( - TextNode textNode, - int index, - int length, - String text, { - Attributes? attributes, - }) { - var newAttributes = attributes; - if (index != 0 && attributes == null) { - newAttributes = - textNode.delta.slice(max(index - 1, 0), index).first.attributes; - if (newAttributes == null) { - final slicedDelta = textNode.delta.slice(index, index + length); - if (slicedDelta.isNotEmpty) { - newAttributes = slicedDelta.first.attributes; - } - } - } - updateText( - textNode, - Delta() - ..retain(index) - ..delete(length) - ..insert(text, attributes: {...newAttributes ?? {}}), - ); - afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: index + text.length, - ), - ); - } - - void replaceTexts( - List textNodes, - Selection selection, - List texts, - ) { - if (textNodes.isEmpty || texts.isEmpty) { - return; - } - - if (textNodes.length == texts.length) { - final length = textNodes.length; - - if (length == 1) { - replaceText( - textNodes.first, - selection.startIndex, - selection.endIndex - selection.startIndex, - texts.first, - ); - return; - } - - for (var i = 0; i < textNodes.length; i++) { - final textNode = textNodes[i]; - if (i == 0) { - replaceText( - textNode, - selection.startIndex, - textNode.toPlainText().length, - texts.first, - ); - } else if (i == length - 1) { - replaceText( - textNode, - 0, - selection.endIndex, - texts.last, - ); - } else { - replaceText( - textNode, - 0, - textNode.toPlainText().length, - texts[i], - ); - } - } - return; - } - - if (textNodes.length > texts.length) { - final length = textNodes.length; - for (var i = 0; i < textNodes.length; i++) { - final textNode = textNodes[i]; - if (i == 0) { - replaceText( - textNode, - selection.startIndex, - textNode.toPlainText().length, - texts.first, - ); - } else if (i == length - 1 && texts.length >= 2) { - replaceText( - textNode, - 0, - selection.endIndex, - texts.last, - ); - } else if (i < texts.length - 1) { - replaceText( - textNode, - 0, - textNode.toPlainText().length, - texts[i], - ); - } else { - deleteNode(textNode); - if (i == textNodes.length - 1) { - final delta = Delta() - ..insert(texts[0]) - ..addAll( - textNodes.last.delta.slice(selection.end.offset), - ); - replaceText( - textNode, - selection.start.offset, - texts[0].length, - delta.toPlainText(), - ); - } - } - } - afterSelection = null; - return; - } - - if (textNodes.length < texts.length) { - final length = texts.length; - var path = textNodes.first.path; - - for (var i = 0; i < texts.length; i++) { - final text = texts[i]; - if (i == 0) { - replaceText( - textNodes.first, - selection.startIndex, - textNodes.first.toPlainText().length, - text, - ); - path = path.next; - } else if (i == length - 1 && textNodes.length >= 2) { - replaceText( - textNodes.last, - 0, - selection.endIndex, - text, - ); - path = path.next; - } else { - if (i < textNodes.length - 1) { - replaceText( - textNodes[i], - 0, - textNodes[i].toPlainText().length, - text, - ); - path = path.next; - } else { - if (i == texts.length - 1) { - final delta = Delta() - ..insert(text) - ..addAll( - textNodes.last.delta.slice(selection.end.offset), - ); - insertNode( - path, - TextNode( - delta: delta, - ), - ); - } else { - insertNode( - path, - TextNode( - delta: Delta()..insert(text), - ), - ); - } - } - } - } - afterSelection = null; - return; - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/editor_state.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/editor_state.dart deleted file mode 100644 index 4b214adad6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/editor_state.dart +++ /dev/null @@ -1,232 +0,0 @@ -import 'dart:async'; -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/infra/log.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; -import 'package:appflowy_editor/src/render/style/editor_style.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart'; -import 'package:appflowy_editor/src/service/service.dart'; -import 'package:flutter/material.dart'; - -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/core/document/document.dart'; -import 'package:appflowy_editor/src/core/transform/operation.dart'; -import 'package:appflowy_editor/src/core/transform/transaction.dart'; -import 'package:appflowy_editor/src/history/undo_manager.dart'; - -class ApplyOptions { - /// This flag indicates that - /// whether the transaction should be recorded into - /// the undo stack - final bool recordUndo; - final bool recordRedo; - const ApplyOptions({ - this.recordUndo = true, - this.recordRedo = false, - }); -} - -enum CursorUpdateReason { - uiEvent, - others, -} - -/// The state of the editor. -/// -/// The state includes: -/// - The document to render -/// - The state of the selection -/// -/// [EditorState] also includes the services of the editor: -/// - Selection service -/// - Scroll service -/// - Keyboard service -/// - Input service -/// - Toolbar service -/// -/// In consideration of collaborative editing, -/// all the mutations should be applied through [Transaction]. -/// -/// Mutating the document with document's API is not recommended. -class EditorState { - final Document document; - - // Service reference. - final service = FlowyService(); - - /// Configures log output parameters, - /// such as log level and log output callbacks, - /// with this variable. - LogConfiguration get logConfiguration => LogConfiguration(); - - /// Stores the selection menu items. - List selectionMenuItems = []; - - /// Stores the toolbar items. - List toolbarItems = []; - - /// Operation stream. - Stream get transactionStream => _observer.stream; - final StreamController _observer = StreamController.broadcast(); - - late ThemeData themeData; - EditorStyle get editorStyle => - themeData.extension() ?? EditorStyle.light; - - final UndoManager undoManager = UndoManager(); - Selection? _cursorSelection; - - // TODO: only for testing. - bool disableSealTimer = false; - bool disableRules = false; - - bool editable = true; - - Transaction get transaction { - final transaction = Transaction(document: document); - transaction.beforeSelection = _cursorSelection; - return transaction; - } - - Selection? get cursorSelection { - return _cursorSelection; - } - - RenderBox? get renderBox { - final renderObject = - service.scrollServiceKey.currentContext?.findRenderObject(); - if (renderObject != null && renderObject is RenderBox) { - return renderObject; - } - return null; - } - - Future updateCursorSelection( - Selection? cursorSelection, [ - CursorUpdateReason reason = CursorUpdateReason.others, - ]) { - final completer = Completer(); - - // broadcast to other users here - if (reason != CursorUpdateReason.uiEvent) { - service.selectionService.updateSelection(cursorSelection); - } - _cursorSelection = cursorSelection; - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - completer.complete(); - }); - return completer.future; - } - - Timer? _debouncedSealHistoryItemTimer; - - EditorState({ - required this.document, - }) { - undoManager.state = this; - } - - factory EditorState.empty() { - return EditorState(document: Document.empty()); - } - - /// Apply the transaction to the state. - /// - /// The options can be used to determine whether the editor - /// should record the transaction in undo/redo stack. - Future apply( - Transaction transaction, { - ApplyOptions options = const ApplyOptions(recordUndo: true), - ruleCount = 0, - withUpdateCursor = true, - }) async { - final completer = Completer(); - - if (!editable) { - completer.complete(); - return completer.future; - } - // TODO: validate the transaction. - for (final op in transaction.operations) { - _applyOperation(op); - } - - _observer.add(transaction); - - WidgetsBinding.instance.addPostFrameCallback((_) async { - _applyRules(ruleCount); - if (withUpdateCursor) { - await updateCursorSelection(transaction.afterSelection); - } - completer.complete(); - }); - - if (options.recordUndo) { - final undoItem = undoManager.getUndoHistoryItem(); - undoItem.addAll(transaction.operations); - if (undoItem.beforeSelection == null && - transaction.beforeSelection != null) { - undoItem.beforeSelection = transaction.beforeSelection; - } - undoItem.afterSelection = transaction.afterSelection; - _debouncedSealHistoryItem(); - } else if (options.recordRedo) { - final redoItem = HistoryItem(); - redoItem.addAll(transaction.operations); - redoItem.beforeSelection = transaction.beforeSelection; - redoItem.afterSelection = transaction.afterSelection; - undoManager.redoStack.push(redoItem); - } - - return completer.future; - } - - void _debouncedSealHistoryItem() { - if (disableSealTimer) { - return; - } - _debouncedSealHistoryItemTimer?.cancel(); - _debouncedSealHistoryItemTimer = - Timer(const Duration(milliseconds: 1000), () { - if (undoManager.undoStack.isNonEmpty) { - Log.editor.debug('Seal history item'); - final last = undoManager.undoStack.last; - last.seal(); - } - }); - } - - void _applyOperation(Operation op) { - if (op is InsertOperation) { - document.insert(op.path, op.nodes); - } else if (op is UpdateOperation) { - document.update(op.path, op.attributes); - } else if (op is DeleteOperation) { - document.delete(op.path, op.nodes.length); - } else if (op is UpdateTextOperation) { - document.updateText(op.path, op.delta); - } - } - - void _applyRules(int ruleCount) { - // Set a maximum count to prevent a dead loop. - if (ruleCount >= 5 || disableRules) { - return; - } - - final tr = transaction; - - // Rules - _insureLastNodeEditable(tr); - - if (tr.operations.isNotEmpty) { - apply(tr, ruleCount: ruleCount + 1, withUpdateCursor: false); - } - } - - void _insureLastNodeEditable(Transaction tr) { - if (document.root.children.isEmpty || - document.root.children.last.id != 'text') { - tr.insertNode([document.root.children.length], TextNode.empty()); - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart deleted file mode 100644 index 816747fdad..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; -import 'package:flutter/material.dart'; - -extension NodeAttributesExtensions on Attributes { - String? get heading { - if (containsKey(BuiltInAttributeKey.subtype) && - containsKey(BuiltInAttributeKey.heading) && - this[BuiltInAttributeKey.subtype] == BuiltInAttributeKey.heading && - this[BuiltInAttributeKey.heading] is String) { - return this[BuiltInAttributeKey.heading]; - } - return null; - } - - bool get quote { - return containsKey(BuiltInAttributeKey.quote); - } - - num? get number { - if (containsKey(BuiltInAttributeKey.number) && - this[BuiltInAttributeKey.number] is num) { - return this[BuiltInAttributeKey.number]; - } - return null; - } - - bool get code { - if (containsKey(BuiltInAttributeKey.code) && - this[BuiltInAttributeKey.code] is bool) { - return this[BuiltInAttributeKey.code]; - } - return false; - } - - bool get check { - if (containsKey(BuiltInAttributeKey.checkbox) && - this[BuiltInAttributeKey.checkbox] is bool) { - return this[BuiltInAttributeKey.checkbox]; - } - return false; - } -} - -extension DeltaAttributesExtensions on Attributes { - bool get bold { - return (containsKey(BuiltInAttributeKey.bold) && - this[BuiltInAttributeKey.bold] == true); - } - - bool get italic { - return (containsKey(BuiltInAttributeKey.italic) && - this[BuiltInAttributeKey.italic] == true); - } - - bool get underline { - return (containsKey(BuiltInAttributeKey.underline) && - this[BuiltInAttributeKey.underline] == true); - } - - bool get strikethrough { - return (containsKey(BuiltInAttributeKey.strikethrough) && - this[BuiltInAttributeKey.strikethrough] == true); - } - - static const whiteInt = 0XFFFFFFFF; - - Color? get color { - if (containsKey(BuiltInAttributeKey.color) && - this[BuiltInAttributeKey.color] is String) { - return Color( - // If the parse fails returns white by default - int.tryParse(this[BuiltInAttributeKey.color]) ?? whiteInt, - ); - } - return null; - } - - Color? get backgroundColor { - if (containsKey(BuiltInAttributeKey.backgroundColor) && - this[BuiltInAttributeKey.backgroundColor] is String) { - return Color( - int.tryParse(this[BuiltInAttributeKey.backgroundColor]) ?? whiteInt); - } - return null; - } - - String? get href { - if (containsKey(BuiltInAttributeKey.href) && - this[BuiltInAttributeKey.href] is String) { - return this[BuiltInAttributeKey.href]; - } - return null; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/color_extension.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/color_extension.dart deleted file mode 100644 index 7228127104..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/color_extension.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/painting.dart'; - -extension ColorExtension on Color { - /// Try to parse the `rgba(red, greed, blue, alpha)` - /// from the string. - static Color? tryFromRgbaString(String colorString) { - final reg = RegExp(r'rgba\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)'); - final match = reg.firstMatch(colorString); - if (match == null) { - return null; - } - - if (match.groupCount < 4) { - return null; - } - final redStr = match.group(1); - final greenStr = match.group(2); - final blueStr = match.group(3); - final alphaStr = match.group(4); - - final red = redStr != null ? int.tryParse(redStr) : null; - final green = greenStr != null ? int.tryParse(greenStr) : null; - final blue = blueStr != null ? int.tryParse(blueStr) : null; - final alpha = alphaStr != null ? int.tryParse(alphaStr) : null; - - if (red == null || green == null || blue == null || alpha == null) { - return null; - } - - return Color.fromARGB(alpha, red, green, blue); - } - - String toRgbaString() { - return 'rgba($red, $green, $blue, $alpha)'; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/editor_state_extensions.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/editor_state_extensions.dart deleted file mode 100644 index 56b0c7726f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/editor_state_extensions.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; - -extension EditorStateExtensions on EditorState { - List get selectedTextNodes => - service.selectionService.currentSelectedNodes - .whereType() - .toList(growable: false); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/node_extensions.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/node_extensions.dart deleted file mode 100644 index 0a89ecc4ef..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/node_extensions.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/extensions/object_extensions.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:flutter/material.dart'; - -extension NodeExtensions on Node { - RenderBox? get renderBox => - key.currentContext?.findRenderObject()?.unwrapOrNull(); - - BuildContext? get context => key.currentContext; - SelectableMixin? get selectable => - key.currentState?.unwrapOrNull(); - - bool inSelection(Selection selection) { - if (selection.start.path <= selection.end.path) { - return selection.start.path <= path && path <= selection.end.path; - } else { - return selection.end.path <= path && path <= selection.start.path; - } - } - - Rect get rect { - if (renderBox != null) { - final boxOffset = renderBox!.localToGlobal(Offset.zero); - return boxOffset & renderBox!.size; - } - return Rect.zero; - } - - bool isSelected(EditorState editorState) { - final currentSelectedNodes = - editorState.service.selectionService.currentSelectedNodes; - return currentSelectedNodes.length == 1 && - currentSelectedNodes.first == this; - } -} - -extension NodesExtensions on List { - List get normalized { - if (isEmpty) { - return this; - } - - if (first.path > last.path) { - return reversed.toList(); - } - - return this; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/object_extensions.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/object_extensions.dart deleted file mode 100644 index b1b6e53512..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/object_extensions.dart +++ /dev/null @@ -1,8 +0,0 @@ -extension FlowyObjectExtensions on Object { - T? unwrapOrNull() { - if (this is T) { - return this as T; - } - return null; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart deleted file mode 100644 index 93ddeb11b9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart +++ /dev/null @@ -1,216 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; - -extension TextNodeExtension on TextNode { - T? getAttributeInSelection(Selection selection, String styleKey) { - final ops = delta.whereType(); - final startOffset = - selection.isBackward ? selection.start.offset : selection.end.offset; - final endOffset = - selection.isBackward ? selection.end.offset : selection.start.offset; - var start = 0; - for (final op in ops) { - if (start >= endOffset) { - break; - } - final length = op.length; - if (start < endOffset && start + length > startOffset) { - final attributes = op.attributes; - if (attributes != null && attributes[styleKey] is T?) { - return attributes[styleKey]; - } - } - start += length; - } - return null; - } - - bool allSatisfyLinkInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.href, (value) { - return value != null; - }); - - bool allSatisfyFontColorInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.color, (value) { - return value != null; - }); - - bool allSatisfyBackgroundColorInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.backgroundColor, - (value) { - return value != null; - }); - - bool allSatisfyBoldInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.bold, (value) { - return value == true; - }); - - bool allSatisfyItalicInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.italic, (value) { - return value == true; - }); - - bool allSatisfyUnderlineInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.underline, (value) { - return value == true; - }); - - bool allSatisfyStrikethroughInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.strikethrough, - (value) { - return value == true; - }); - - bool allSatisfyCodeInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.code, (value) { - return value == true; - }); - - bool allSatisfyInSelection( - Selection selection, - String styleKey, - bool Function(dynamic value) test, - ) { - if (BuiltInAttributeKey.globalStyleKeys.contains(styleKey)) { - if (attributes.containsKey(styleKey)) { - return test(attributes[styleKey]); - } - } else if (BuiltInAttributeKey.partialStyleKeys.contains(styleKey)) { - final ops = delta.whereType(); - final startOffset = - selection.isBackward ? selection.start.offset : selection.end.offset; - final endOffset = - selection.isBackward ? selection.end.offset : selection.start.offset; - var start = 0; - for (final op in ops) { - if (start >= endOffset) { - break; - } - final length = op.length; - if (start < endOffset && start + length > startOffset) { - if (op.attributes == null || - !op.attributes!.containsKey(styleKey) || - !test(op.attributes![styleKey])) { - return false; - } - } - start += length; - } - return true; - } - return false; - } - - bool allNotSatisfyInSelection( - String styleKey, - dynamic value, - Selection selection, - ) { - final ops = delta.whereType(); - final startOffset = - selection.isBackward ? selection.start.offset : selection.end.offset; - final endOffset = - selection.isBackward ? selection.end.offset : selection.start.offset; - var start = 0; - for (final op in ops) { - if (start >= endOffset) { - break; - } - final length = op.length; - if (start < endOffset && start + length > startOffset) { - if (op.attributes != null && - op.attributes!.containsKey(styleKey) && - op.attributes![styleKey] == value) { - return false; - } - } - start += length; - } - return true; - } -} - -extension TextNodesExtension on List { - bool allSatisfyBoldInSelection(Selection selection) => allSatisfyInSelection( - selection, - BuiltInAttributeKey.bold, - (value) => value == true, - ); - - bool allSatisfyItalicInSelection(Selection selection) => - allSatisfyInSelection( - selection, - BuiltInAttributeKey.italic, - (value) => value == true, - ); - - bool allSatisfyUnderlineInSelection(Selection selection) => - allSatisfyInSelection( - selection, - BuiltInAttributeKey.underline, - (value) => value == true, - ); - - bool allSatisfyStrikethroughInSelection(Selection selection) => - allSatisfyInSelection( - selection, - BuiltInAttributeKey.strikethrough, - (value) => value == true, - ); - - bool allSatisfyInSelection( - Selection selection, - String styleKey, - bool Function(dynamic value) test, - ) { - if (isEmpty) { - return false; - } - if (length == 1) { - return first.allSatisfyInSelection(selection, styleKey, (value) { - return test(value); - }); - } else { - for (var i = 0; i < length; i++) { - final node = this[i]; - final Selection newSelection; - if (i == 0 && node.path.equals(selection.start.path)) { - if (selection.isBackward) { - newSelection = selection.copyWith( - end: Position(path: node.path, offset: node.toPlainText().length), - ); - } else { - newSelection = selection.copyWith( - end: Position(path: node.path, offset: 0), - ); - } - } else if (i == length - 1 && node.path.equals(selection.end.path)) { - if (selection.isBackward) { - newSelection = selection.copyWith( - start: Position(path: node.path, offset: 0), - ); - } else { - newSelection = selection.copyWith( - start: - Position(path: node.path, offset: node.toPlainText().length), - ); - } - } else { - newSelection = Selection( - start: Position(path: node.path, offset: 0), - end: Position(path: node.path, offset: node.toPlainText().length), - ); - } - if (!node.allSatisfyInSelection(newSelection, styleKey, test)) { - return false; - } - } - return true; - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/text_style_extension.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/text_style_extension.dart deleted file mode 100644 index 5e2828bf07..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/text_style_extension.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -extension TextSpanExtensions on TextSpan { - TextSpan copyWith({ - String? text, - TextStyle? style, - List? children, - GestureRecognizer? recognizer, - String? semanticsLabel, - }) { - return TextSpan( - text: text ?? this.text, - style: style ?? this.style, - children: children ?? this.children, - recognizer: recognizer ?? this.recognizer, - semanticsLabel: semanticsLabel ?? this.semanticsLabel, - ); - } - - TextSpan updateTextStyle(TextStyle? other) { - if (other == null) { - return this; - } - return copyWith( - style: style?.combine(other), - children: children?.map((child) { - if (child is TextSpan) { - return child.updateTextStyle(other); - } - return child; - }).toList(growable: false), - ); - } -} - -extension TextStyleExtensions on TextStyle { - TextStyle combine(TextStyle? other) { - if (other == null) { - return this; - } - if (!other.inherit) { - return other; - } - - return copyWith( - color: other.color, - backgroundColor: other.backgroundColor, - fontSize: other.fontSize, - fontWeight: other.fontWeight, - fontStyle: other.fontStyle, - letterSpacing: other.letterSpacing, - wordSpacing: other.wordSpacing, - textBaseline: other.textBaseline, - height: other.height, - leadingDistribution: other.leadingDistribution, - locale: other.locale, - foreground: other.foreground, - background: other.background, - shadows: other.shadows, - fontFeatures: other.fontFeatures, - decoration: TextDecoration.combine([ - if (decoration != null) decoration!, - if (other.decoration != null) other.decoration!, - ]), - decorationColor: other.decorationColor, - decorationStyle: other.decorationStyle, - decorationThickness: other.decorationThickness, - fontFamilyFallback: other.fontFamilyFallback, - overflow: other.overflow, - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/theme_extension.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/theme_extension.dart deleted file mode 100644 index 9b8f01aafa..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/theme_extension.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -extension ThemeExtension on ThemeData { - T? extensionOrNull() { - if (extensions.containsKey(T)) { - return extensions[T] as T; - } - return null; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/url_launcher_extension.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/url_launcher_extension.dart deleted file mode 100644 index 1c0ea30c82..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/extensions/url_launcher_extension.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:url_launcher/url_launcher_string.dart'; - -Future safeLaunchUrl(String? href) async { - if (href == null) { - return Future.value(false); - } - final uri = Uri.parse(href); - // url_launcher cannot open a link without scheme. - final newHref = (uri.scheme.isNotEmpty ? href : 'http://$href').trim(); - if (await canLaunchUrlString(newHref)) { - await launchUrlString(newHref); - } - return Future.value(true); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/flutter/overlay.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/flutter/overlay.dart deleted file mode 100644 index 0a91229e0a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/flutter/overlay.dart +++ /dev/null @@ -1,904 +0,0 @@ -// TODO: Remove this file until we update the flutter version to 3.5.x -// -// This file is copied from flutter(3.5.x) repo. -// -// We Need to commit(https://github.com/flutter/flutter/pull/113770) to fix the -// overflow issue. - -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:collection'; -import 'dart:math' as math; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; - -/// A place in an [Overlay] that can contain a widget. -/// -/// Overlay entries are inserted into an [Overlay] using the -/// [OverlayState.insert] or [OverlayState.insertAll] functions. To find the -/// closest enclosing overlay for a given [BuildContext], use the [Overlay.of] -/// function. -/// -/// An overlay entry can be in at most one overlay at a time. To remove an entry -/// from its overlay, call the [remove] function on the overlay entry. -/// -/// Because an [Overlay] uses a [Stack] layout, overlay entries can use -/// [Positioned] and [AnimatedPositioned] to position themselves within the -/// overlay. -/// -/// For example, [Draggable] uses an [OverlayEntry] to show the drag avatar that -/// follows the user's finger across the screen after the drag begins. Using the -/// overlay to display the drag avatar lets the avatar float over the other -/// widgets in the app. As the user's finger moves, draggable calls -/// [markNeedsBuild] on the overlay entry to cause it to rebuild. In its build, -/// the entry includes a [Positioned] with its top and left property set to -/// position the drag avatar near the user's finger. When the drag is over, -/// [Draggable] removes the entry from the overlay to remove the drag avatar -/// from view. -/// -/// By default, if there is an entirely [opaque] entry over this one, then this -/// one will not be included in the widget tree (in particular, stateful widgets -/// within the overlay entry will not be instantiated). To ensure that your -/// overlay entry is still built even if it is not visible, set [maintainState] -/// to true. This is more expensive, so should be done with care. In particular, -/// if widgets in an overlay entry with [maintainState] set to true repeatedly -/// call [State.setState], the user's battery will be drained unnecessarily. -/// -/// [OverlayEntry] is a [ChangeNotifier] that notifies when the widget built by -/// [builder] is mounted or unmounted, whose exact state can be queried by -/// [mounted]. -/// -/// See also: -/// -/// * [Overlay] -/// * [OverlayState] -/// * [WidgetsApp] -/// * [MaterialApp] -class OverlayEntry extends ChangeNotifier { - /// Creates an overlay entry. - /// - /// To insert the entry into an [Overlay], first find the overlay using - /// [Overlay.of] and then call [OverlayState.insert]. To remove the entry, - /// call [remove] on the overlay entry itself. - OverlayEntry({ - required this.builder, - bool opaque = false, - bool maintainState = false, - }) : _opaque = opaque, - _maintainState = maintainState; - - /// This entry will include the widget built by this builder in the overlay at - /// the entry's position. - /// - /// To cause this builder to be called again, call [markNeedsBuild] on this - /// overlay entry. - final WidgetBuilder builder; - - /// Whether this entry occludes the entire overlay. - /// - /// If an entry claims to be opaque, then, for efficiency, the overlay will - /// skip building entries below that entry unless they have [maintainState] - /// set. - bool get opaque => _opaque; - bool _opaque; - set opaque(bool value) { - if (_opaque == value) return; - _opaque = value; - _overlay?._didChangeEntryOpacity(); - } - - /// Whether this entry must be included in the tree even if there is a fully - /// [opaque] entry above it. - /// - /// By default, if there is an entirely [opaque] entry over this one, then this - /// one will not be included in the widget tree (in particular, stateful widgets - /// within the overlay entry will not be instantiated). To ensure that your - /// overlay entry is still built even if it is not visible, set [maintainState] - /// to true. This is more expensive, so should be done with care. In particular, - /// if widgets in an overlay entry with [maintainState] set to true repeatedly - /// call [State.setState], the user's battery will be drained unnecessarily. - /// - /// This is used by the [Navigator] and [Route] objects to ensure that routes - /// are kept around even when in the background, so that [Future]s promised - /// from subsequent routes will be handled properly when they complete. - bool get maintainState => _maintainState; - bool _maintainState; - set maintainState(bool value) { - if (_maintainState == value) return; - _maintainState = value; - assert(_overlay != null); - _overlay!._didChangeEntryOpacity(); - } - - /// Whether the [OverlayEntry] is currently mounted in the widget tree. - /// - /// The [OverlayEntry] notifies its listeners when this value changes. - bool get mounted => _mounted; - bool _mounted = false; - void _updateMounted(bool value) { - if (value == _mounted) { - return; - } - _mounted = value; - notifyListeners(); - } - - OverlayState? _overlay; - final GlobalKey<_OverlayEntryWidgetState> _key = - GlobalKey<_OverlayEntryWidgetState>(); - - /// Remove this entry from the overlay. - /// - /// This should only be called once. - /// - /// This method removes this overlay entry from the overlay immediately. The - /// UI will be updated in the same frame if this method is called before the - /// overlay rebuild in this frame; otherwise, the UI will be updated in the - /// next frame. This means that it is safe to call during builds, but also - /// that if you do call this after the overlay rebuild, the UI will not update - /// until the next frame (i.e. many milliseconds later). - void remove() { - assert(_overlay != null); - final OverlayState overlay = _overlay!; - _overlay = null; - if (!overlay.mounted) return; - - overlay._entries.remove(this); - if (SchedulerBinding.instance.schedulerPhase == - SchedulerPhase.persistentCallbacks) { - SchedulerBinding.instance.addPostFrameCallback((Duration duration) { - overlay._markDirty(); - }); - } else { - overlay._markDirty(); - } - } - - /// Cause this entry to rebuild during the next pipeline flush. - /// - /// You need to call this function if the output of [builder] has changed. - void markNeedsBuild() { - _key.currentState?._markNeedsBuild(); - } - - @override - String toString() => - '${describeIdentity(this)}(opaque: $opaque; maintainState: $maintainState)'; -} - -class _OverlayEntryWidget extends StatefulWidget { - const _OverlayEntryWidget({ - required Key key, - required this.entry, - this.tickerEnabled = true, - }) : super(key: key); - - final OverlayEntry entry; - final bool tickerEnabled; - - @override - _OverlayEntryWidgetState createState() => _OverlayEntryWidgetState(); -} - -class _OverlayEntryWidgetState extends State<_OverlayEntryWidget> { - @override - void initState() { - super.initState(); - widget.entry._updateMounted(true); - } - - @override - void dispose() { - widget.entry._updateMounted(false); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return TickerMode( - enabled: widget.tickerEnabled, - child: widget.entry.builder(context), - ); - } - - void _markNeedsBuild() { - setState(() {/* the state that changed is in the builder */}); - } -} - -/// A stack of entries that can be managed independently. -/// -/// Overlays let independent child widgets "float" visual elements on top of -/// other widgets by inserting them into the overlay's stack. The overlay lets -/// each of these widgets manage their participation in the overlay using -/// [OverlayEntry] objects. -/// -/// Although you can create an [Overlay] directly, it's most common to use the -/// overlay created by the [Navigator] in a [WidgetsApp] or a [MaterialApp]. The -/// navigator uses its overlay to manage the visual appearance of its routes. -/// -/// The [Overlay] widget uses a custom stack implementation, which is very -/// similar to the [Stack] widget. The main use case of [Overlay] is related to -/// navigation and being able to insert widgets on top of the pages in an app. -/// To simply display a stack of widgets, consider using [Stack] instead. -/// -/// See also: -/// -/// * [OverlayEntry], the class that is used for describing the overlay entries. -/// * [OverlayState], which is used to insert the entries into the overlay. -/// * [WidgetsApp], which inserts an [Overlay] widget indirectly via its [Navigator]. -/// * [MaterialApp], which inserts an [Overlay] widget indirectly via its [Navigator]. -/// * [Stack], which allows directly displaying a stack of widgets. -class Overlay extends StatefulWidget { - /// Creates an overlay. - /// - /// The initial entries will be inserted into the overlay when its associated - /// [OverlayState] is initialized. - /// - /// Rather than creating an overlay, consider using the overlay that is - /// created by the [Navigator] in a [WidgetsApp] or a [MaterialApp] for the application. - const Overlay({ - Key? key, - this.initialEntries = const [], - this.clipBehavior = Clip.hardEdge, - }) : super(key: key); - - /// The entries to include in the overlay initially. - /// - /// These entries are only used when the [OverlayState] is initialized. If you - /// are providing a new [Overlay] description for an overlay that's already in - /// the tree, then the new entries are ignored. - /// - /// To add entries to an [Overlay] that is already in the tree, use - /// [Overlay.of] to obtain the [OverlayState] (or assign a [GlobalKey] to the - /// [Overlay] widget and obtain the [OverlayState] via - /// [GlobalKey.currentState]), and then use [OverlayState.insert] or - /// [OverlayState.insertAll]. - /// - /// To remove an entry from an [Overlay], use [OverlayEntry.remove]. - final List initialEntries; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge], and must not be null. - final Clip clipBehavior; - - /// The state from the closest instance of this class that encloses the given context. - /// - /// In debug mode, if the `debugRequiredFor` argument is provided then this - /// function will assert that an overlay was found and will throw an exception - /// if not. The exception attempts to explain that the calling [Widget] (the - /// one given by the `debugRequiredFor` argument) needs an [Overlay] to be - /// present to function. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// OverlayState overlay = Overlay.of(context); - /// ``` - /// - /// If `rootOverlay` is set to true, the state from the furthest instance of - /// this class is given instead. Useful for installing overlay entries - /// above all subsequent instances of [Overlay]. - /// - /// This method can be expensive (it walks the element tree). - static OverlayState? of( - BuildContext context, { - bool rootOverlay = false, - Widget? debugRequiredFor, - }) { - final OverlayState? result = rootOverlay - ? context.findRootAncestorStateOfType() - : context.findAncestorStateOfType(); - assert(() { - if (debugRequiredFor != null && result == null) { - final List information = [ - ErrorSummary('No Overlay widget found.'), - ErrorDescription( - '${debugRequiredFor.runtimeType} widgets require an Overlay widget ancestor for correct operation.'), - ErrorHint( - 'The most common way to add an Overlay to an application is to include a MaterialApp or Navigator widget in the runApp() call.'), - DiagnosticsProperty( - 'The specific widget that failed to find an overlay was', - debugRequiredFor, - style: DiagnosticsTreeStyle.errorProperty), - if (context.widget != debugRequiredFor) - context.describeElement( - 'The context from which that widget was searching for an overlay was'), - ]; - - throw FlutterError.fromParts(information); - } - return true; - }()); - return result; - } - - @override - OverlayState createState() => OverlayState(); -} - -/// The current state of an [Overlay]. -/// -/// Used to insert [OverlayEntry]s into the overlay using the [insert] and -/// [insertAll] functions. -class OverlayState extends State with TickerProviderStateMixin { - final List _entries = []; - - @override - void initState() { - super.initState(); - insertAll(widget.initialEntries); - } - - int _insertionIndex(OverlayEntry? below, OverlayEntry? above) { - assert(above == null || below == null); - if (below != null) return _entries.indexOf(below); - if (above != null) return _entries.indexOf(above) + 1; - return _entries.length; - } - - /// Insert the given entry into the overlay. - /// - /// If `below` is non-null, the entry is inserted just below `below`. - /// If `above` is non-null, the entry is inserted just above `above`. - /// Otherwise, the entry is inserted on top. - /// - /// It is an error to specify both `above` and `below`. - void insert(OverlayEntry entry, {OverlayEntry? below, OverlayEntry? above}) { - assert(_debugVerifyInsertPosition(above, below)); - assert(!_entries.contains(entry), - 'The specified entry is already present in the Overlay.'); - assert(entry._overlay == null, - 'The specified entry is already present in another Overlay.'); - entry._overlay = this; - setState(() { - _entries.insert(_insertionIndex(below, above), entry); - }); - } - - /// Insert all the entries in the given iterable. - /// - /// If `below` is non-null, the entries are inserted just below `below`. - /// If `above` is non-null, the entries are inserted just above `above`. - /// Otherwise, the entries are inserted on top. - /// - /// It is an error to specify both `above` and `below`. - void insertAll(Iterable entries, - {OverlayEntry? below, OverlayEntry? above}) { - assert(_debugVerifyInsertPosition(above, below)); - assert( - entries.every((OverlayEntry entry) => !_entries.contains(entry)), - 'One or more of the specified entries are already present in the Overlay.', - ); - assert( - entries.every((OverlayEntry entry) => entry._overlay == null), - 'One or more of the specified entries are already present in another Overlay.', - ); - if (entries.isEmpty) return; - for (final OverlayEntry entry in entries) { - assert(entry._overlay == null); - entry._overlay = this; - } - setState(() { - _entries.insertAll(_insertionIndex(below, above), entries); - }); - } - - bool _debugVerifyInsertPosition(OverlayEntry? above, OverlayEntry? below, - {Iterable? newEntries}) { - assert( - above == null || below == null, - 'Only one of `above` and `below` may be specified.', - ); - assert( - above == null || - (above._overlay == this && - _entries.contains(above) && - (newEntries?.contains(above) ?? true)), - 'The provided entry used for `above` must be present in the Overlay${newEntries != null ? ' and in the `newEntriesList`' : ''}.', - ); - assert( - below == null || - (below._overlay == this && - _entries.contains(below) && - (newEntries?.contains(below) ?? true)), - 'The provided entry used for `below` must be present in the Overlay${newEntries != null ? ' and in the `newEntriesList`' : ''}.', - ); - return true; - } - - /// Remove all the entries listed in the given iterable, then reinsert them - /// into the overlay in the given order. - /// - /// Entries mention in `newEntries` but absent from the overlay are inserted - /// as if with [insertAll]. - /// - /// Entries not mentioned in `newEntries` but present in the overlay are - /// positioned as a group in the resulting list relative to the entries that - /// were moved, as specified by one of `below` or `above`, which, if - /// specified, must be one of the entries in `newEntries`: - /// - /// If `below` is non-null, the group is positioned just below `below`. - /// If `above` is non-null, the group is positioned just above `above`. - /// Otherwise, the group is left on top, with all the rearranged entries - /// below. - /// - /// It is an error to specify both `above` and `below`. - void rearrange(Iterable newEntries, - {OverlayEntry? below, OverlayEntry? above}) { - final List newEntriesList = newEntries is List - ? newEntries - : newEntries.toList(growable: false); - assert( - _debugVerifyInsertPosition(above, below, newEntries: newEntriesList)); - assert( - newEntriesList.every((OverlayEntry entry) => - entry._overlay == null || entry._overlay == this), - 'One or more of the specified entries are already present in another Overlay.', - ); - assert( - newEntriesList.every((OverlayEntry entry) => - _entries.indexOf(entry) == _entries.lastIndexOf(entry)), - 'One or more of the specified entries are specified multiple times.', - ); - if (newEntriesList.isEmpty) return; - if (listEquals(_entries, newEntriesList)) return; - final LinkedHashSet old = - LinkedHashSet.of(_entries); - for (final OverlayEntry entry in newEntriesList) { - entry._overlay ??= this; - } - setState(() { - _entries.clear(); - _entries.addAll(newEntriesList); - old.removeAll(newEntriesList); - _entries.insertAll(_insertionIndex(below, above), old); - }); - } - - void _markDirty() { - if (mounted) { - setState(() {}); - } - } - - /// (DEBUG ONLY) Check whether a given entry is visible (i.e., not behind an - /// opaque entry). - /// - /// This is an O(N) algorithm, and should not be necessary except for debug - /// asserts. To avoid people depending on it, this function is implemented - /// only in debug mode, and always returns false in release mode. - bool debugIsVisible(OverlayEntry entry) { - bool result = false; - assert(_entries.contains(entry)); - assert(() { - for (int i = _entries.length - 1; i > 0; i -= 1) { - final OverlayEntry candidate = _entries[i]; - if (candidate == entry) { - result = true; - break; - } - if (candidate.opaque) break; - } - return true; - }()); - return result; - } - - void _didChangeEntryOpacity() { - setState(() { - // We use the opacity of the entry in our build function, which means we - // our state has changed. - }); - } - - @override - Widget build(BuildContext context) { - // This list is filled backwards and then reversed below before - // it is added to the tree. - final List children = []; - bool onstage = true; - int onstageCount = 0; - for (int i = _entries.length - 1; i >= 0; i -= 1) { - final OverlayEntry entry = _entries[i]; - if (onstage) { - onstageCount += 1; - children.add(_OverlayEntryWidget( - key: entry._key, - entry: entry, - )); - if (entry.opaque) onstage = false; - } else if (entry.maintainState) { - children.add(_OverlayEntryWidget( - key: entry._key, - entry: entry, - tickerEnabled: false, - )); - } - } - return _Theatre( - skipCount: children.length - onstageCount, - clipBehavior: widget.clipBehavior, - children: children.reversed.toList(growable: false), - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - // TODO(jacobr): use IterableProperty instead as that would - // provide a slightly more consistent string summary of the List. - properties - .add(DiagnosticsProperty>('entries', _entries)); - } -} - -/// Special version of a [Stack], that doesn't layout and render the first -/// [skipCount] children. -/// -/// The first [skipCount] children are considered "offstage". -class _Theatre extends MultiChildRenderObjectWidget { - _Theatre({ - Key? key, - this.skipCount = 0, - this.clipBehavior = Clip.hardEdge, - List children = const [], - }) : assert(skipCount >= 0), - assert(children.length >= skipCount), - super(key: key, children: children); - - final int skipCount; - - final Clip clipBehavior; - - @override - _TheatreElement createElement() => _TheatreElement(this); - - @override - _RenderTheatre createRenderObject(BuildContext context) { - return _RenderTheatre( - skipCount: skipCount, - textDirection: Directionality.of(context), - clipBehavior: clipBehavior, - ); - } - - @override - void updateRenderObject(BuildContext context, _RenderTheatre renderObject) { - renderObject - ..skipCount = skipCount - ..textDirection = Directionality.of(context) - ..clipBehavior = clipBehavior; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(IntProperty('skipCount', skipCount)); - } -} - -class _TheatreElement extends MultiChildRenderObjectElement { - _TheatreElement(_Theatre widget) : super(widget); - - @override - _RenderTheatre get renderObject => super.renderObject as _RenderTheatre; - - @override - void debugVisitOnstageChildren(ElementVisitor visitor) { - final _Theatre theatre = widget as _Theatre; - assert(children.length >= theatre.skipCount); - children.skip(theatre.skipCount).forEach(visitor); - } -} - -class _RenderTheatre extends RenderBox - with ContainerRenderObjectMixin { - _RenderTheatre({ - List? children, - required TextDirection textDirection, - int skipCount = 0, - Clip clipBehavior = Clip.hardEdge, - }) : assert(skipCount >= 0), - _textDirection = textDirection, - _skipCount = skipCount, - _clipBehavior = clipBehavior { - addAll(children); - } - - bool _hasVisualOverflow = false; - - @override - void setupParentData(RenderBox child) { - if (child.parentData is! StackParentData) { - child.parentData = StackParentData(); - } - } - - Alignment? _resolvedAlignment; - - void _resolve() { - if (_resolvedAlignment != null) return; - _resolvedAlignment = AlignmentDirectional.topStart.resolve(textDirection); - } - - void _markNeedResolution() { - _resolvedAlignment = null; - markNeedsLayout(); - } - - TextDirection get textDirection => _textDirection; - TextDirection _textDirection; - set textDirection(TextDirection value) { - if (_textDirection == value) return; - _textDirection = value; - _markNeedResolution(); - } - - int get skipCount => _skipCount; - int _skipCount; - set skipCount(int value) { - if (_skipCount != value) { - _skipCount = value; - markNeedsLayout(); - } - } - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge], and must not be null. - Clip get clipBehavior => _clipBehavior; - Clip _clipBehavior = Clip.hardEdge; - set clipBehavior(Clip value) { - if (value != _clipBehavior) { - _clipBehavior = value; - markNeedsPaint(); - markNeedsSemanticsUpdate(); - } - } - - RenderBox? get _firstOnstageChild { - if (skipCount == super.childCount) { - return null; - } - RenderBox? child = super.firstChild; - for (int toSkip = skipCount; toSkip > 0; toSkip--) { - final StackParentData childParentData = - child!.parentData! as StackParentData; - child = childParentData.nextSibling; - assert(child != null); - } - return child; - } - - RenderBox? get _lastOnstageChild => - skipCount == super.childCount ? null : lastChild; - - int get _onstageChildCount => childCount - skipCount; - - @override - double computeMinIntrinsicWidth(double height) { - return RenderStack.getIntrinsicDimension(_firstOnstageChild, - (RenderBox child) => child.getMinIntrinsicWidth(height)); - } - - @override - double computeMaxIntrinsicWidth(double height) { - return RenderStack.getIntrinsicDimension(_firstOnstageChild, - (RenderBox child) => child.getMaxIntrinsicWidth(height)); - } - - @override - double computeMinIntrinsicHeight(double width) { - return RenderStack.getIntrinsicDimension(_firstOnstageChild, - (RenderBox child) => child.getMinIntrinsicHeight(width)); - } - - @override - double computeMaxIntrinsicHeight(double width) { - return RenderStack.getIntrinsicDimension(_firstOnstageChild, - (RenderBox child) => child.getMaxIntrinsicHeight(width)); - } - - @override - double? computeDistanceToActualBaseline(TextBaseline baseline) { - assert(!debugNeedsLayout); - double? result; - RenderBox? child = _firstOnstageChild; - while (child != null) { - assert(!child.debugNeedsLayout); - final StackParentData childParentData = - child.parentData! as StackParentData; - double? candidate = child.getDistanceToActualBaseline(baseline); - if (candidate != null) { - candidate += childParentData.offset.dy; - if (result != null) { - result = math.min(result, candidate); - } else { - result = candidate; - } - } - child = childParentData.nextSibling; - } - return result; - } - - @override - bool get sizedByParent => true; - - @override - Size computeDryLayout(BoxConstraints constraints) { - assert(constraints.biggest.isFinite); - return constraints.biggest; - } - - @override - void performLayout() { - _hasVisualOverflow = false; - - if (_onstageChildCount == 0) { - return; - } - - _resolve(); - assert(_resolvedAlignment != null); - - // Same BoxConstraints as used by RenderStack for StackFit.expand. - final BoxConstraints nonPositionedConstraints = - BoxConstraints.tight(constraints.biggest); - - RenderBox? child = _firstOnstageChild; - while (child != null) { - final StackParentData childParentData = - child.parentData! as StackParentData; - - if (!childParentData.isPositioned) { - child.layout(nonPositionedConstraints, parentUsesSize: true); - childParentData.offset = - _resolvedAlignment!.alongOffset(size - child.size as Offset); - } else { - _hasVisualOverflow = RenderStack.layoutPositionedChild( - child, childParentData, size, _resolvedAlignment!) || - _hasVisualOverflow; - } - - assert(child.parentData == childParentData); - child = childParentData.nextSibling; - } - } - - @override - bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { - RenderBox? child = _lastOnstageChild; - for (int i = 0; i < _onstageChildCount; i++) { - assert(child != null); - final StackParentData childParentData = - child!.parentData! as StackParentData; - final bool isHit = result.addWithPaintOffset( - offset: childParentData.offset, - position: position, - hitTest: (BoxHitTestResult result, Offset transformed) { - assert(transformed == position - childParentData.offset); - return child!.hitTest(result, position: transformed); - }, - ); - if (isHit) return true; - child = childParentData.previousSibling; - } - return false; - } - - @protected - void paintStack(PaintingContext context, Offset offset) { - RenderBox? child = _firstOnstageChild; - while (child != null) { - final StackParentData childParentData = - child.parentData! as StackParentData; - context.paintChild(child, childParentData.offset + offset); - child = childParentData.nextSibling; - } - } - - @override - void paint(PaintingContext context, Offset offset) { - _hasVisualOverflow = true; - if (_hasVisualOverflow && clipBehavior != Clip.none) { - _clipRectLayer.layer = context.pushClipRect( - needsCompositing, - offset, - Offset.zero & size, - paintStack, - clipBehavior: clipBehavior, - oldLayer: _clipRectLayer.layer, - ); - } else { - _clipRectLayer.layer = null; - paintStack(context, offset); - } - } - - final LayerHandle _clipRectLayer = - LayerHandle(); - - @override - void dispose() { - _clipRectLayer.layer = null; - super.dispose(); - } - - @override - void visitChildrenForSemantics(RenderObjectVisitor visitor) { - RenderBox? child = _firstOnstageChild; - while (child != null) { - visitor(child); - final StackParentData childParentData = - child.parentData! as StackParentData; - child = childParentData.nextSibling; - } - } - - @override - Rect? describeApproximatePaintClip(RenderObject child) => - _hasVisualOverflow ? Offset.zero & size : null; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(IntProperty('skipCount', skipCount)); - properties.add(EnumProperty('textDirection', textDirection)); - } - - @override - List debugDescribeChildren() { - final List offstageChildren = []; - final List onstageChildren = []; - - int count = 1; - bool onstage = false; - RenderBox? child = firstChild; - final RenderBox? firstOnstageChild = _firstOnstageChild; - while (child != null) { - if (child == firstOnstageChild) { - onstage = true; - count = 1; - } - - if (onstage) { - onstageChildren.add( - child.toDiagnosticsNode( - name: 'onstage $count', - ), - ); - } else { - offstageChildren.add( - child.toDiagnosticsNode( - name: 'offstage $count', - style: DiagnosticsTreeStyle.offstage, - ), - ); - } - - final StackParentData childParentData = - child.parentData! as StackParentData; - child = childParentData.nextSibling; - count += 1; - } - - return [ - ...onstageChildren, - if (offstageChildren.isNotEmpty) - ...offstageChildren - else - DiagnosticsNode.message( - 'no offstage children', - style: DiagnosticsTreeStyle.offstage, - ), - ]; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/history/undo_manager.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/history/undo_manager.dart deleted file mode 100644 index fa4ad0492d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/history/undo_manager.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/infra/log.dart'; -import 'package:appflowy_editor/src/core/transform/operation.dart'; -import 'package:appflowy_editor/src/core/transform/transaction.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; - -/// A [HistoryItem] contains list of operations committed by users. -/// If a [HistoryItem] is not sealed, operations can be added sequentially. -/// Otherwise, the operations should be added to a new [HistoryItem]. -class HistoryItem extends LinkedListEntry { - final List operations = []; - Selection? beforeSelection; - Selection? afterSelection; - bool _sealed = false; - - HistoryItem(); - - /// Seal the history item. - /// When an item is sealed, no more operations can be added - /// to the item. - /// - /// The caller should create a new [HistoryItem]. - seal() { - _sealed = true; - } - - bool get sealed => _sealed; - - add(Operation op) { - operations.add(op); - } - - addAll(Iterable iterable) { - operations.addAll(iterable); - } - - /// Create a new [Transaction] by inverting the operations. - Transaction toTransaction(EditorState state) { - final builder = Transaction(document: state.document); - for (var i = operations.length - 1; i >= 0; i--) { - final operation = operations[i]; - final inverted = operation.invert(); - builder.add(inverted, transform: false); - } - builder.afterSelection = beforeSelection; - builder.beforeSelection = afterSelection; - return builder; - } -} - -class FixedSizeStack { - final _list = LinkedList(); - final int maxSize; - - FixedSizeStack(this.maxSize); - - push(HistoryItem stackItem) { - if (_list.length >= maxSize) { - _list.remove(_list.first); - } - _list.add(stackItem); - } - - HistoryItem? pop() { - if (_list.isEmpty) { - return null; - } - final last = _list.last; - - _list.remove(last); - - return last; - } - - clear() { - _list.clear(); - } - - HistoryItem get last => _list.last; - - bool get isEmpty => _list.isEmpty; - - bool get isNonEmpty => _list.isNotEmpty; -} - -class UndoManager { - final FixedSizeStack undoStack; - final FixedSizeStack redoStack; - EditorState? state; - - UndoManager([int stackSize = 20]) - : undoStack = FixedSizeStack(stackSize), - redoStack = FixedSizeStack(stackSize); - - HistoryItem getUndoHistoryItem() { - if (undoStack.isEmpty) { - final item = HistoryItem(); - undoStack.push(item); - return item; - } - final last = undoStack.last; - if (last.sealed) { - redoStack.clear(); - final item = HistoryItem(); - undoStack.push(item); - return item; - } - return last; - } - - undo() { - Log.editor.debug('undo'); - final s = state; - if (s == null) { - return; - } - final historyItem = undoStack.pop(); - if (historyItem == null) { - return; - } - final transaction = historyItem.toTransaction(s); - s.apply( - transaction, - options: const ApplyOptions( - recordUndo: false, - recordRedo: true, - ), - ); - } - - redo() { - Log.editor.debug('redo'); - final s = state; - if (s == null) { - return; - } - final historyItem = redoStack.pop(); - if (historyItem == null) { - return; - } - final transaction = historyItem.toTransaction(s); - s.apply( - transaction, - options: const ApplyOptions( - recordUndo: true, - recordRedo: false, - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/clipboard.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/clipboard.dart deleted file mode 100644 index d7f692f2fe..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/clipboard.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'dart:io' show Platform; - -import 'package:rich_clipboard/rich_clipboard.dart'; - -class AppFlowyClipboardData { - const AppFlowyClipboardData({ - required this.text, - required this.html, - }); - final String? text; - final String? html; -} - -class AppFlowyClipboard { - static Future setData({ - String? text, - String? html, - }) async { - // https://github.com/BringingFire/rich_clipboard/issues/13 - // Wrapping a `` tag for html in Windows, - // otherwise it will raise an exception - if (Platform.isWindows && html != null) { - if (!html.startsWith('')) { - html = '$html'; - } - } - - return RichClipboard.setData( - RichClipboardData( - text: text, - html: html, - ), - ); - } - - static Future getData() async { - final data = await RichClipboard.getData(); - final text = data.text; - var html = data.html; - - // https://github.com/BringingFire/rich_clipboard/issues/13 - // Remove all the fragment symbol in Windows. - if (Platform.isWindows && html != null) { - html = html - .replaceAll('', '') - .replaceAll('', ''); - } - - return AppFlowyClipboardData( - text: text, - html: html, - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/flowy_svg.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/flowy_svg.dart deleted file mode 100644 index dd42e1714c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/flowy_svg.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; - -class FlowySvg extends StatelessWidget { - const FlowySvg({ - Key? key, - this.name, - this.width, - this.height, - this.color, - this.number, - this.padding, - }) : super(key: key); - - final String? name; - final double? width; - final double? height; - final Color? color; - final int? number; - final EdgeInsets? padding; - - final _defaultWidth = 20.0; - final _defaultHeight = 20.0; - - @override - Widget build(BuildContext context) { - return Padding( - padding: padding ?? const EdgeInsets.all(0), - child: _buildSvg(), - ); - } - - Widget _buildSvg() { - if (name != null) { - return SvgPicture.asset( - 'assets/images/$name.svg', - colorFilter: - color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null, - fit: BoxFit.fill, - height: height, - width: width, - package: 'appflowy_editor', - ); - } else if (number != null) { - final numberText = - '$number.'; - return SvgPicture.string( - numberText, - width: width ?? _defaultWidth, - height: height ?? _defaultHeight, - ); - } - return Container(); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/html_converter.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/html_converter.dart deleted file mode 100644 index f20d3d8a9a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/html_converter.dart +++ /dev/null @@ -1,626 +0,0 @@ -import 'dart:collection'; -import 'dart:ui'; - -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; -import 'package:appflowy_editor/src/extensions/color_extension.dart'; -import 'package:flutter/material.dart'; -import 'package:html/parser.dart' show parse; -import 'package:html/dom.dart' as html; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; - -class HTMLTag { - static const h1 = "h1"; - static const h2 = "h2"; - static const h3 = "h3"; - static const orderedList = "ol"; - static const unorderedList = "ul"; - static const list = "li"; - static const paragraph = "p"; - static const image = "img"; - static const anchor = "a"; - static const italic = "i"; - static const bold = "b"; - static const underline = "u"; - static const del = "del"; - static const strong = "strong"; - static const span = "span"; - static const code = "code"; - static const blockQuote = "blockquote"; - static const div = "div"; - - static bool isTopLevel(String tag) { - return tag == h1 || - tag == h2 || - tag == h3 || - tag == paragraph || - tag == div || - tag == blockQuote; - } -} - -/// Converting the HTML to nodes -class HTMLToNodesConverter { - final html.Document _document; - - /// This flag is used for parsing HTML pasting from Google Docs - /// Google docs wraps the the content inside the `` tag. It's strange. - /// - /// If a `` element is parsing in the

, we regard it as as text spans. - /// Otherwise, it's parsed as a container. - bool _inParagraph = false; - - HTMLToNodesConverter(String htmlString) : _document = parse(htmlString); - - List toNodes() { - final childNodes = _document.body?.nodes.toList() ?? []; - return _handleContainer(childNodes); - } - - List _handleContainer(List childNodes) { - final delta = Delta(); - final result = []; - for (final child in childNodes) { - if (child is html.Element) { - if (child.localName == HTMLTag.anchor || - child.localName == HTMLTag.span || - child.localName == HTMLTag.code || - child.localName == HTMLTag.strong || - child.localName == HTMLTag.underline || - child.localName == HTMLTag.italic || - child.localName == HTMLTag.del) { - _handleRichTextElement(delta, child); - } else if (child.localName == HTMLTag.bold) { - // Google docs wraps the the content inside the `` tag. - // It's strange - if (!_inParagraph) { - result.addAll(_handleBTag(child)); - } else { - result.add(_handleRichText(child)); - } - } else if (child.localName == HTMLTag.blockQuote) { - result.addAll(_handleBlockQuote(child)); - } else { - result.addAll(_handleElement(child)); - } - } else { - delta.insert(child.text ?? ""); - } - } - if (delta.isNotEmpty) { - result.add(TextNode(delta: delta)); - } - return result; - } - - List _handleBlockQuote(html.Element element) { - final result = []; - - for (final child in element.nodes.toList()) { - if (child is html.Element) { - result.addAll( - _handleElement(child, {"subtype": BuiltInAttributeKey.quote})); - } - } - - return result; - } - - List _handleBTag(html.Element element) { - final childNodes = element.nodes; - return _handleContainer(childNodes); - } - - List _handleElement(html.Element element, - [Map? attributes]) { - if (element.localName == HTMLTag.h1) { - return [_handleHeadingElement(element, HTMLTag.h1)]; - } else if (element.localName == HTMLTag.h2) { - return [_handleHeadingElement(element, HTMLTag.h2)]; - } else if (element.localName == HTMLTag.h3) { - return [_handleHeadingElement(element, HTMLTag.h3)]; - } else if (element.localName == HTMLTag.unorderedList) { - return _handleUnorderedList(element); - } else if (element.localName == HTMLTag.orderedList) { - return _handleOrderedList(element); - } else if (element.localName == HTMLTag.list) { - return _handleListElement(element); - } else if (element.localName == HTMLTag.paragraph) { - return [_handleParagraph(element, attributes)]; - } else if (element.localName == HTMLTag.image) { - return [_handleImage(element)]; - } else { - final delta = Delta(); - delta.insert(element.text); - if (delta.isNotEmpty) { - return [TextNode(delta: delta)]; - } - } - return []; - } - - Node _handleParagraph(html.Element element, - [Map? attributes]) { - _inParagraph = true; - final node = _handleRichText(element, attributes); - _inParagraph = false; - return node; - } - - Map _cssStringToMap(String? cssString) { - final result = {}; - if (cssString == null) { - return result; - } - - final entries = cssString.split(";"); - for (final entry in entries) { - final tuples = entry.split(":"); - if (tuples.length < 2) { - continue; - } - result[tuples[0].trim()] = tuples[1].trim(); - } - - return result; - } - - Attributes? _getDeltaAttributesFromHtmlAttributes( - LinkedHashMap htmlAttributes) { - final attrs = {}; - final styleString = htmlAttributes["style"]; - final cssMap = _cssStringToMap(styleString); - - final fontWeightStr = cssMap["font-weight"]; - if (fontWeightStr != null) { - if (fontWeightStr == "bold") { - attrs[BuiltInAttributeKey.bold] = true; - } else { - int? weight = int.tryParse(fontWeightStr); - if (weight != null && weight > 500) { - attrs[BuiltInAttributeKey.bold] = true; - } - } - } - - final textDecorationStr = cssMap["text-decoration"]; - if (textDecorationStr != null) { - _assignTextDecorations(attrs, textDecorationStr); - } - - final backgroundColorStr = cssMap["background-color"]; - final backgroundColor = backgroundColorStr == null - ? null - : ColorExtension.tryFromRgbaString(backgroundColorStr); - if (backgroundColor != null) { - attrs[BuiltInAttributeKey.backgroundColor] = - '0x${backgroundColor.value.toRadixString(16)}'; - } - - if (cssMap["font-style"] == "italic") { - attrs[BuiltInAttributeKey.italic] = true; - } - - return attrs.isEmpty ? null : attrs; - } - - _assignTextDecorations(Attributes attrs, String decorationStr) { - final decorations = decorationStr.split(" "); - for (final d in decorations) { - if (d == "line-through") { - attrs[BuiltInAttributeKey.strikethrough] = true; - } else if (d == "underline") { - attrs[BuiltInAttributeKey.underline] = true; - } - } - } - - _handleRichTextElement(Delta delta, html.Element element) { - if (element.localName == HTMLTag.span) { - delta.insert( - element.text, - attributes: _getDeltaAttributesFromHtmlAttributes(element.attributes), - ); - } else if (element.localName == HTMLTag.anchor) { - final hyperLink = element.attributes["href"]; - Map? attributes; - if (hyperLink != null) { - attributes = {"href": hyperLink}; - } - delta.insert(element.text, attributes: attributes); - } else if (element.localName == HTMLTag.strong || - element.localName == HTMLTag.bold) { - delta.insert(element.text, attributes: {BuiltInAttributeKey.bold: true}); - } else if (element.localName == HTMLTag.underline) { - delta.insert(element.text, - attributes: {BuiltInAttributeKey.underline: true}); - } else if (element.localName == HTMLTag.italic) { - delta - .insert(element.text, attributes: {BuiltInAttributeKey.italic: true}); - } else if (element.localName == HTMLTag.del) { - delta.insert(element.text, - attributes: {BuiltInAttributeKey.strikethrough: true}); - } else if (element.localName == HTMLTag.code) { - delta.insert(element.text, attributes: {BuiltInAttributeKey.code: true}); - } else { - delta.insert(element.text); - } - } - - /// A container contains a will - /// be regarded as a checkbox block. - /// - /// A container contains a will be regarded as a image block - Node _handleRichText(html.Element element, - [Map? attributes]) { - final image = element.querySelector(HTMLTag.image); - if (image != null) { - final imageNode = _handleImage(image); - return imageNode; - } - final testInput = element.querySelector("input"); - bool checked = false; - final isCheckbox = - testInput != null && testInput.attributes["type"] == "checkbox"; - if (isCheckbox) { - checked = testInput.attributes.containsKey("checked") && - testInput.attributes["checked"] != "false"; - } - - final delta = Delta(); - - for (final child in element.nodes.toList()) { - if (child is html.Element) { - _handleRichTextElement(delta, child); - } else { - delta.insert(child.text ?? ""); - } - } - - final textNode = TextNode(delta: delta, attributes: { - if (attributes != null) ...attributes, - if (isCheckbox) ...{ - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: checked, - } - }); - return textNode; - } - - Node _handleImage(html.Element element) { - final src = element.attributes["src"]; - final attributes = {}; - if (src != null) { - attributes["image_src"] = src; - } - return Node(type: "image", attributes: attributes, children: LinkedList()); - } - - List _handleUnorderedList(html.Element element) { - final result = []; - for (var child in element.children) { - result.addAll(_handleListElement( - child, {"subtype": BuiltInAttributeKey.bulletedList})); - } - return result; - } - - List _handleOrderedList(html.Element element) { - final result = []; - for (var i = 0; i < element.children.length; i++) { - final child = element.children[i]; - result.addAll(_handleListElement( - child, {"subtype": BuiltInAttributeKey.numberList, "number": i + 1})); - } - return result; - } - - Node _handleHeadingElement( - html.Element element, - String headingStyle, - ) { - final delta = Delta(); - delta.insert(element.text); - return TextNode( - attributes: {"subtype": "heading", "heading": headingStyle}, - delta: delta); - } - - List _handleListElement(html.Element element, - [Map? attributes]) { - final result = []; - final childNodes = element.nodes.toList(); - for (final child in childNodes) { - if (child is html.Element) { - result.addAll(_handleElement(child, attributes)); - } - } - return result; - } -} - -/// [NodesToHTMLConverter] is used to convert the nodes to HTML. -/// Can be used to copy & paste, exporting the document. -class NodesToHTMLConverter { - final List nodes; - final int? startOffset; - final int? endOffset; - final List _result = []; - - /// According to the W3C specs. The bullet list should be wrapped as - /// - ///

    - ///
  • xxx
  • - ///
  • xxx
  • - ///
  • xxx
  • - ///
- /// - /// This container is used to save the list elements temporarily. - html.Element? _stashListContainer; - - NodesToHTMLConverter( - {required this.nodes, this.startOffset, this.endOffset}) { - if (nodes.isEmpty) { - return; - } else if (nodes.length == 1) { - final first = nodes.first; - if (first is TextNode) { - nodes[0] = first.copyWith( - delta: first.delta.slice(startOffset ?? 0, endOffset)); - } - } else { - final first = nodes.first; - final last = nodes.last; - if (first is TextNode) { - nodes[0] = first.copyWith(delta: first.delta.slice(startOffset ?? 0)); - } - if (last is TextNode) { - nodes[nodes.length - 1] = - last.copyWith(delta: last.delta.slice(0, endOffset)); - } - } - } - - List toHTMLNodes() { - for (final node in nodes) { - if (node.type == "text") { - final textNode = node as TextNode; - if (node == nodes.first) { - _addTextNode(textNode); - } else if (node == nodes.last) { - _addTextNode(textNode, end: endOffset); - } else { - _addTextNode(textNode); - } - } - // TODO: handle image and other blocks - } - if (_stashListContainer != null) { - _result.add(_stashListContainer!); - _stashListContainer = null; - } - return _result; - } - - _addTextNode(TextNode textNode, {int? end}) { - _addElement(textNode, _textNodeToHtml(textNode, end: end)); - } - - _addElement(TextNode textNode, html.Element element) { - if (element.localName == HTMLTag.list) { - final isNumbered = - textNode.attributes["subtype"] == BuiltInAttributeKey.numberList; - _stashListContainer ??= html.Element.tag( - isNumbered ? HTMLTag.orderedList : HTMLTag.unorderedList); - _stashListContainer?.append(element); - } else { - if (_stashListContainer != null) { - _result.add(_stashListContainer!); - _stashListContainer = null; - } - _result.add(element); - } - } - - String toHTMLString() { - final elements = toHTMLNodes(); - final copyString = elements.fold( - "", ((previousValue, element) => previousValue + stringify(element))); - return copyString; - } - - html.Element _textNodeToHtml(TextNode textNode, {int? end}) { - String? subType = textNode.attributes["subtype"]; - String? heading = textNode.attributes["heading"]; - return _deltaToHtml(textNode.delta, - subType: subType, - heading: heading, - end: end, - checked: textNode.attributes["checkbox"] == true); - } - - String _textDecorationsFromAttributes(Attributes attributes) { - var textDecoration = []; - if (attributes[BuiltInAttributeKey.strikethrough] == true) { - textDecoration.add("line-through"); - } - if (attributes[BuiltInAttributeKey.underline] == true) { - textDecoration.add("underline"); - } - - return textDecoration.join(" "); - } - - String _attributesToCssStyle(Map attributes) { - final cssMap = {}; - if (attributes[BuiltInAttributeKey.backgroundColor] != null) { - final color = Color( - int.parse(attributes[BuiltInAttributeKey.backgroundColor]), - ); - cssMap["background-color"] = color.toRgbaString(); - } - if (attributes[BuiltInAttributeKey.color] != null) { - final color = Color( - int.parse(attributes[BuiltInAttributeKey.color]), - ); - cssMap["color"] = color.toRgbaString(); - } - if (attributes[BuiltInAttributeKey.bold] == true) { - cssMap["font-weight"] = "bold"; - } - - final textDecoration = _textDecorationsFromAttributes(attributes); - if (textDecoration.isNotEmpty) { - cssMap["text-decoration"] = textDecoration; - } - - if (attributes[BuiltInAttributeKey.italic] == true) { - cssMap["font-style"] = "italic"; - } - return _cssMapToCssStyle(cssMap); - } - - String _cssMapToCssStyle(Map cssMap) { - return cssMap.entries.fold("", (previousValue, element) { - final kv = '${element.key}: ${element.value}'; - if (previousValue.isEmpty) { - return kv; - } - return '$previousValue; $kv'; - }); - } - - /// Convert the rich text to HTML - /// - /// Use `` for bold only. - /// Use `` for italic only. - /// Use `` for strikethrough only. - /// Use `` for underline only. - /// - /// If the text has multiple styles, use a `` - /// to mix the styles. - /// - /// A CSS style string is used to describe the styles. - /// The HTML will be: - /// - /// ```html - /// Text - /// ``` - html.Element _deltaToHtml(Delta delta, - {String? subType, String? heading, int? end, bool? checked}) { - if (end != null) { - delta = delta.slice(0, end); - } - - final childNodes = []; - String tagName = HTMLTag.paragraph; - - if (subType == BuiltInAttributeKey.bulletedList || - subType == BuiltInAttributeKey.numberList) { - tagName = HTMLTag.list; - } else if (subType == BuiltInAttributeKey.checkbox) { - final node = html.Element.html(''); - if (checked != null && checked) { - node.attributes["checked"] = "true"; - } - childNodes.add(node); - } else if (subType == BuiltInAttributeKey.heading) { - if (heading == BuiltInAttributeKey.h1) { - tagName = HTMLTag.h1; - } else if (heading == BuiltInAttributeKey.h2) { - tagName = HTMLTag.h2; - } else if (heading == BuiltInAttributeKey.h3) { - tagName = HTMLTag.h3; - } - } else if (subType == BuiltInAttributeKey.quote) { - tagName = HTMLTag.blockQuote; - } - - for (final op in delta) { - if (op is TextInsert) { - final attributes = op.attributes; - if (attributes != null) { - if (attributes.length == 1 && - attributes[BuiltInAttributeKey.bold] == true) { - final strong = html.Element.tag(HTMLTag.strong); - strong.append(html.Text(op.text)); - childNodes.add(strong); - } else if (attributes.length == 1 && - attributes[BuiltInAttributeKey.underline] == true) { - final strong = html.Element.tag(HTMLTag.underline); - strong.append(html.Text(op.text)); - childNodes.add(strong); - } else if (attributes.length == 1 && - attributes[BuiltInAttributeKey.italic] == true) { - final strong = html.Element.tag(HTMLTag.italic); - strong.append(html.Text(op.text)); - childNodes.add(strong); - } else if (attributes.length == 1 && - attributes[BuiltInAttributeKey.strikethrough] == true) { - final strong = html.Element.tag(HTMLTag.del); - strong.append(html.Text(op.text)); - childNodes.add(strong); - } else if (attributes.length == 1 && - attributes[BuiltInAttributeKey.code] == true) { - final code = html.Element.tag(HTMLTag.code); - code.append(html.Text(op.text)); - childNodes.add(code); - } else if (attributes.length == 1 && - attributes[BuiltInAttributeKey.href] != null) { - final anchor = html.Element.tag(HTMLTag.anchor); - anchor.attributes["href"] = attributes[BuiltInAttributeKey.href]; - anchor.append(html.Text(op.text)); - childNodes.add(anchor); - } else { - final span = html.Element.tag(HTMLTag.span); - final cssString = _attributesToCssStyle(attributes); - if (cssString.isNotEmpty) { - span.attributes["style"] = cssString; - } - span.append(html.Text(op.text)); - childNodes.add(span); - } - } else { - childNodes.add(html.Text(op.text)); - } - } - } - - if (tagName == HTMLTag.blockQuote) { - final p = html.Element.tag(HTMLTag.paragraph); - for (final node in childNodes) { - p.append(node); - } - final blockQuote = html.Element.tag(tagName); - blockQuote.append(p); - return blockQuote; - } else if (!HTMLTag.isTopLevel(tagName)) { - final p = html.Element.tag(HTMLTag.paragraph); - for (final node in childNodes) { - p.append(node); - } - final result = html.Element.tag(HTMLTag.list); - result.append(p); - return result; - } else { - final p = html.Element.tag(tagName); - for (final node in childNodes) { - p.append(node); - } - return p; - } - } -} - -String stringify(html.Node node) { - if (node is html.Element) { - return node.outerHtml; - } - - if (node is html.Text) { - return node.text; - } - - return ""; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/infra.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/infra.dart deleted file mode 100644 index 1463f6a97b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/infra.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; - -class Infra { -// find the forward nearest text node - static TextNode? forwardNearestTextNode(Node node) { - var previous = node.previous; - while (previous != null) { - final lastTextNode = findLastTextNode(previous); - if (lastTextNode != null) { - return lastTextNode; - } - if (previous is TextNode) { - return previous; - } - previous = previous.previous; - } - final parent = node.parent; - if (parent != null) { - if (parent is TextNode) { - return parent; - } - return forwardNearestTextNode(parent); - } - return null; - } - - // find the last text node - static TextNode? findLastTextNode(Node node) { - final children = node.children.toList(growable: false).reversed; - for (final child in children) { - if (child.children.isNotEmpty) { - final result = findLastTextNode(child); - if (result != null) { - return result; - } - } - if (child is TextNode) { - return child; - } - } - if (node is TextNode) { - return node; - } - return null; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/log.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/log.dart deleted file mode 100644 index 8175ecb705..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/infra/log.dart +++ /dev/null @@ -1,143 +0,0 @@ -import 'package:logging/logging.dart'; - -enum LogLevel { - off, - error, - warn, - info, - debug, - all, -} - -typedef LogHandler = void Function(String message); - -/// Manages log service for [AppFlowyEditor] -/// -/// Set the log level and config the handler depending on your need. -class LogConfiguration { - LogConfiguration._() { - Logger.root.onRecord.listen((record) { - if (handler != null) { - handler!( - '[${record.level.toLogLevel().name}][${record.loggerName}]: ${record.time}: ${record.message}', - ); - } - }); - } - - factory LogConfiguration() => _logConfiguration; - - static final LogConfiguration _logConfiguration = LogConfiguration._(); - - LogHandler? handler; - - LogLevel _level = LogLevel.off; - - LogLevel get level => _level; - set level(LogLevel level) { - _level = level; - Logger.root.level = level.toLevel(); - } -} - -/// For logging message in AppFlowyEditor -class Log { - Log._({ - required this.name, - }) : _logger = Logger(name); - - final String name; - late final Logger _logger; - - /// For logging message related to [AppFlowyEditor]. - /// - /// For example, uses the logger when registering plugins - /// or handling something related to [EditorState]. - static Log editor = Log._(name: 'editor'); - - /// For logging message related to [AppFlowySelectionService]. - /// - /// For example, uses the logger when updating or clearing selection. - static Log selection = Log._(name: 'selection'); - - /// For logging message related to [AppFlowyKeyboardService]. - /// - /// For example, uses the logger when processing shortcut events. - static Log keyboard = Log._(name: 'keyboard'); - - /// For logging message related to [AppFlowyInputService]. - /// - /// For example, uses the logger when processing text inputs. - static Log input = Log._(name: 'input'); - - /// For logging message related to [AppFlowyScrollService]. - /// - /// For example, uses the logger when processing scroll events. - static Log scroll = Log._(name: 'scroll'); - - /// For logging message related to [AppFlowyToolbarService]. - /// - /// For example, uses the logger when processing toolbar events. - static Log toolbar = Log._(name: 'toolbar'); - - /// For logging message related to UI. - /// - /// For example, uses the logger when building the widget. - static Log ui = Log._(name: 'ui'); - - void error(String message) => _logger.severe(message); - void warn(String message) => _logger.warning(message); - void info(String message) => _logger.info(message); - void debug(String message) => _logger.fine(message); -} - -extension on LogLevel { - Level toLevel() { - switch (this) { - case LogLevel.off: - return Level.OFF; - case LogLevel.error: - return Level.SEVERE; - case LogLevel.warn: - return Level.WARNING; - case LogLevel.info: - return Level.INFO; - case LogLevel.debug: - return Level.FINE; - case LogLevel.all: - return Level.ALL; - } - } - - String get name { - switch (this) { - case LogLevel.off: - return 'OFF'; - case LogLevel.error: - return 'ERROR'; - case LogLevel.warn: - return 'WARN'; - case LogLevel.info: - return 'INFO'; - case LogLevel.debug: - return 'DEBUG'; - case LogLevel.all: - return 'ALL'; - } - } -} - -extension on Level { - LogLevel toLogLevel() { - if (this == Level.SEVERE) { - return LogLevel.error; - } else if (this == Level.WARNING) { - return LogLevel.warn; - } else if (this == Level.INFO) { - return LogLevel.info; - } else if (this == Level.FINE) { - return LogLevel.debug; - } - return LogLevel.off; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart deleted file mode 100644 index 16fd7b9b6b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart +++ /dev/null @@ -1,146 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that looks up messages for specific locales by -// delegating to the appropriate library. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:implementation_imports, file_names, unnecessary_new -// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering -// ignore_for_file:argument_type_not_assignable, invalid_assignment -// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases -// ignore_for_file:comment_references - -import 'dart:async'; - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; -import 'package:intl/src/intl_helpers.dart'; - -import 'messages_bn_BN.dart' as messages_bn_bn; -import 'messages_ca.dart' as messages_ca; -import 'messages_cs-CZ.dart' as messages_cs_cz; -import 'messages_de-DE.dart' as messages_de_de; -import 'messages_en.dart' as messages_en; -import 'messages_es-VE.dart' as messages_es_ve; -import 'messages_fr-CA.dart' as messages_fr_ca; -import 'messages_fr-FR.dart' as messages_fr_fr; -import 'messages_hi-IN.dart' as messages_hi_in; -import 'messages_hu-HU.dart' as messages_hu_hu; -import 'messages_id-ID.dart' as messages_id_id; -import 'messages_it-IT.dart' as messages_it_it; -import 'messages_ja-JP.dart' as messages_ja_jp; -import 'messages_ml_IN.dart' as messages_ml_in; -import 'messages_nl-NL.dart' as messages_nl_nl; -import 'messages_pl-PL.dart' as messages_pl_pl; -import 'messages_pt-BR.dart' as messages_pt_br; -import 'messages_pt-PT.dart' as messages_pt_pt; -import 'messages_ru-RU.dart' as messages_ru_ru; -import 'messages_tr-TR.dart' as messages_tr_tr; -import 'messages_zh-CN.dart' as messages_zh_cn; -import 'messages_zh-TW.dart' as messages_zh_tw; - -typedef Future LibraryLoader(); -Map _deferredLibraries = { - 'bn_BN': () => new Future.value(null), - 'ca': () => new Future.value(null), - 'cs_CZ': () => new Future.value(null), - 'de_DE': () => new Future.value(null), - 'en': () => new Future.value(null), - 'es_VE': () => new Future.value(null), - 'fr_CA': () => new Future.value(null), - 'fr_FR': () => new Future.value(null), - 'hi_IN': () => new Future.value(null), - 'hu_HU': () => new Future.value(null), - 'id_ID': () => new Future.value(null), - 'it_IT': () => new Future.value(null), - 'ja_JP': () => new Future.value(null), - 'ml_IN': () => new Future.value(null), - 'nl_NL': () => new Future.value(null), - 'pl_PL': () => new Future.value(null), - 'pt_BR': () => new Future.value(null), - 'pt_PT': () => new Future.value(null), - 'ru_RU': () => new Future.value(null), - 'tr_TR': () => new Future.value(null), - 'zh_CN': () => new Future.value(null), - 'zh_TW': () => new Future.value(null), -}; - -MessageLookupByLibrary? _findExact(String localeName) { - switch (localeName) { - case 'bn_BN': - return messages_bn_bn.messages; - case 'ca': - return messages_ca.messages; - case 'cs_CZ': - return messages_cs_cz.messages; - case 'de_DE': - return messages_de_de.messages; - case 'en': - return messages_en.messages; - case 'es_VE': - return messages_es_ve.messages; - case 'fr_CA': - return messages_fr_ca.messages; - case 'fr_FR': - return messages_fr_fr.messages; - case 'hi_IN': - return messages_hi_in.messages; - case 'hu_HU': - return messages_hu_hu.messages; - case 'id_ID': - return messages_id_id.messages; - case 'it_IT': - return messages_it_it.messages; - case 'ja_JP': - return messages_ja_jp.messages; - case 'ml_IN': - return messages_ml_in.messages; - case 'nl_NL': - return messages_nl_nl.messages; - case 'pl_PL': - return messages_pl_pl.messages; - case 'pt_BR': - return messages_pt_br.messages; - case 'pt_PT': - return messages_pt_pt.messages; - case 'ru_RU': - return messages_ru_ru.messages; - case 'tr_TR': - return messages_tr_tr.messages; - case 'zh_CN': - return messages_zh_cn.messages; - case 'zh_TW': - return messages_zh_tw.messages; - default: - return null; - } -} - -/// User programs should call this before using [localeName] for messages. -Future initializeMessages(String localeName) async { - var availableLocale = Intl.verifiedLocale( - localeName, (locale) => _deferredLibraries[locale] != null, - onFailure: (_) => null); - if (availableLocale == null) { - return new Future.value(false); - } - var lib = _deferredLibraries[availableLocale]; - await (lib == null ? new Future.value(false) : lib()); - initializeInternalMessageLookup(() => new CompositeMessageLookup()); - messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); - return new Future.value(true); -} - -bool _messagesExistFor(String locale) { - try { - return _findExact(locale) != null; - } catch (e) { - return false; - } -} - -MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { - var actualLocale = - Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); - if (actualLocale == null) return null; - return _findExact(actualLocale); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_bn_BN.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_bn_BN.dart deleted file mode 100644 index 38e1cae590..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_bn_BN.dart +++ /dev/null @@ -1,43 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a bn_BN locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'bn_BN'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("বল্ড ফন্ট"), - "bulletedList": MessageLookupByLibrary.simpleMessage("বুলেট তালিকা"), - "checkbox": MessageLookupByLibrary.simpleMessage("চেকবক্স"), - "embedCode": MessageLookupByLibrary.simpleMessage("এম্বেড কোড"), - "heading1": MessageLookupByLibrary.simpleMessage("শিরোনাম 1"), - "heading2": MessageLookupByLibrary.simpleMessage("শিরোনাম 2"), - "heading3": MessageLookupByLibrary.simpleMessage("শিরোনাম 3"), - "highlight": MessageLookupByLibrary.simpleMessage("হাইলাইট"), - "image": MessageLookupByLibrary.simpleMessage("ইমেজ"), - "italic": MessageLookupByLibrary.simpleMessage("ইটালিক ফন্ট"), - "link": MessageLookupByLibrary.simpleMessage("লিঙ্ক"), - "numberedList": - MessageLookupByLibrary.simpleMessage("সংখ্যাযুক্ত তালিকা"), - "quote": MessageLookupByLibrary.simpleMessage("উদ্ধৃতি"), - "strikethrough": MessageLookupByLibrary.simpleMessage("স্ট্রাইকথ্রু"), - "text": MessageLookupByLibrary.simpleMessage("পাঠ্য"), - "underline": MessageLookupByLibrary.simpleMessage("আন্ডারলাইন") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ca.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ca.dart deleted file mode 100644 index 10c178027b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ca.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a ca locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'ca'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_cs-CZ.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_cs-CZ.dart deleted file mode 100644 index 810fe3888f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_cs-CZ.dart +++ /dev/null @@ -1,44 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a cs_CZ locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'cs_CZ'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("Tučně"), - "bulletedList": - MessageLookupByLibrary.simpleMessage("Odrážkový seznam"), - "checkbox": MessageLookupByLibrary.simpleMessage("Zaškrtávací políčko"), - "embedCode": MessageLookupByLibrary.simpleMessage("Vložit kód"), - "heading1": MessageLookupByLibrary.simpleMessage("Nadpis 1"), - "heading2": MessageLookupByLibrary.simpleMessage("Nadpis 2"), - "heading3": MessageLookupByLibrary.simpleMessage("Nadpis 3"), - "highlight": MessageLookupByLibrary.simpleMessage("Zvýraznění"), - "image": MessageLookupByLibrary.simpleMessage("Obrázek"), - "italic": MessageLookupByLibrary.simpleMessage("Kurzíva"), - "link": MessageLookupByLibrary.simpleMessage("Odkaz"), - "numberedList": - MessageLookupByLibrary.simpleMessage("Číslovaný seznam"), - "quote": MessageLookupByLibrary.simpleMessage("Citace"), - "strikethrough": MessageLookupByLibrary.simpleMessage("Přeškrtnutí"), - "text": MessageLookupByLibrary.simpleMessage("Text"), - "underline": MessageLookupByLibrary.simpleMessage("Podtržení") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_de-DE.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_de-DE.dart deleted file mode 100644 index 7771d43370..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_de-DE.dart +++ /dev/null @@ -1,45 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a de_DE locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'de_DE'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("Fett gedruckt"), - "bulletedList": - MessageLookupByLibrary.simpleMessage("Aufzählungsliste"), - "checkbox": MessageLookupByLibrary.simpleMessage("Kontrollkästchen"), - "embedCode": MessageLookupByLibrary.simpleMessage("Code einbetten"), - "heading1": MessageLookupByLibrary.simpleMessage("Überschrift 1"), - "heading2": MessageLookupByLibrary.simpleMessage("Überschrift 2"), - "heading3": MessageLookupByLibrary.simpleMessage("Überschrift 3"), - "highlight": MessageLookupByLibrary.simpleMessage("Markieren"), - "image": MessageLookupByLibrary.simpleMessage("Bild"), - "italic": MessageLookupByLibrary.simpleMessage("kursiv"), - "link": MessageLookupByLibrary.simpleMessage("Verknüpfung"), - "numberedList": - MessageLookupByLibrary.simpleMessage("NummerierteListe"), - "quote": MessageLookupByLibrary.simpleMessage("zitieren"), - "strikethrough": - MessageLookupByLibrary.simpleMessage("durchgestrichen"), - "text": MessageLookupByLibrary.simpleMessage("Text"), - "underline": MessageLookupByLibrary.simpleMessage("unterstreichen") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_en.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_en.dart deleted file mode 100644 index 9705827d12..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_en.dart +++ /dev/null @@ -1,91 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a en locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'en'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "backgroundColorBlue": - MessageLookupByLibrary.simpleMessage("Blue background"), - "backgroundColorBrown": - MessageLookupByLibrary.simpleMessage("Brown background"), - "backgroundColorDefault": - MessageLookupByLibrary.simpleMessage("Default background"), - "backgroundColorGray": - MessageLookupByLibrary.simpleMessage("Gray background"), - "backgroundColorGreen": - MessageLookupByLibrary.simpleMessage("Green background"), - "backgroundColorOrange": - MessageLookupByLibrary.simpleMessage("Orange background"), - "backgroundColorPink": - MessageLookupByLibrary.simpleMessage("Pink background"), - "backgroundColorPurple": - MessageLookupByLibrary.simpleMessage("Purple background"), - "backgroundColorRed": - MessageLookupByLibrary.simpleMessage("Red background"), - "backgroundColorYellow": - MessageLookupByLibrary.simpleMessage("Yellow background"), - "bold": MessageLookupByLibrary.simpleMessage("Bold"), - "bulletedList": MessageLookupByLibrary.simpleMessage("Bulleted List"), - "checkbox": MessageLookupByLibrary.simpleMessage("Checkbox"), - "color": MessageLookupByLibrary.simpleMessage("Color"), - "embedCode": MessageLookupByLibrary.simpleMessage("Embed Code"), - "fontColorBlue": MessageLookupByLibrary.simpleMessage("Blue"), - "fontColorBrown": MessageLookupByLibrary.simpleMessage("Brown"), - "fontColorDefault": MessageLookupByLibrary.simpleMessage("Default"), - "fontColorGray": MessageLookupByLibrary.simpleMessage("Gray"), - "fontColorGreen": MessageLookupByLibrary.simpleMessage("Green"), - "fontColorOrange": MessageLookupByLibrary.simpleMessage("Orange"), - "fontColorPink": MessageLookupByLibrary.simpleMessage("Pink"), - "fontColorPurple": MessageLookupByLibrary.simpleMessage("Purple"), - "fontColorRed": MessageLookupByLibrary.simpleMessage("Red"), - "fontColorYellow": MessageLookupByLibrary.simpleMessage("Yellow"), - "heading1": MessageLookupByLibrary.simpleMessage("H1"), - "heading2": MessageLookupByLibrary.simpleMessage("H2"), - "heading3": MessageLookupByLibrary.simpleMessage("H3"), - "highlight": MessageLookupByLibrary.simpleMessage("Highlight"), - "image": MessageLookupByLibrary.simpleMessage("Image"), - "italic": MessageLookupByLibrary.simpleMessage("Italic"), - "lightLightTint1": MessageLookupByLibrary.simpleMessage("Purple"), - "lightLightTint2": MessageLookupByLibrary.simpleMessage("Pink"), - "lightLightTint3": MessageLookupByLibrary.simpleMessage("Light Pink"), - "lightLightTint4": MessageLookupByLibrary.simpleMessage("Orange"), - "lightLightTint5": MessageLookupByLibrary.simpleMessage("Yellow"), - "lightLightTint6": MessageLookupByLibrary.simpleMessage("Lime"), - "lightLightTint7": MessageLookupByLibrary.simpleMessage("Green"), - "lightLightTint8": MessageLookupByLibrary.simpleMessage("Aqua"), - "lightLightTint9": MessageLookupByLibrary.simpleMessage("Blue"), - "link": MessageLookupByLibrary.simpleMessage("Link"), - "numberedList": MessageLookupByLibrary.simpleMessage("Numbered List"), - "quote": MessageLookupByLibrary.simpleMessage("Quote"), - "strikethrough": MessageLookupByLibrary.simpleMessage("Strikethrough"), - "text": MessageLookupByLibrary.simpleMessage("Text"), - "tint1": MessageLookupByLibrary.simpleMessage("Tint 1"), - "tint2": MessageLookupByLibrary.simpleMessage("Tint 2"), - "tint3": MessageLookupByLibrary.simpleMessage("Tint 3"), - "tint4": MessageLookupByLibrary.simpleMessage("Tint 4"), - "tint5": MessageLookupByLibrary.simpleMessage("Tint 5"), - "tint6": MessageLookupByLibrary.simpleMessage("Tint 6"), - "tint7": MessageLookupByLibrary.simpleMessage("Tint 7"), - "tint8": MessageLookupByLibrary.simpleMessage("Tint 8"), - "tint9": MessageLookupByLibrary.simpleMessage("Tint 9"), - "underline": MessageLookupByLibrary.simpleMessage("Underline") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_es-VE.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_es-VE.dart deleted file mode 100644 index d5b4cb5b06..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_es-VE.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a es_VE locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'es_VE'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart deleted file mode 100644 index a7239232ed..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a fr_CA locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'fr_CA'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("gras"), - "bulletedList": MessageLookupByLibrary.simpleMessage("liste à puces"), - "checkbox": MessageLookupByLibrary.simpleMessage("case à cocher"), - "embedCode": MessageLookupByLibrary.simpleMessage("incorporer Code"), - "heading1": MessageLookupByLibrary.simpleMessage("en-tête1"), - "heading2": MessageLookupByLibrary.simpleMessage("en-tête2"), - "heading3": MessageLookupByLibrary.simpleMessage("en-tête3"), - "highlight": MessageLookupByLibrary.simpleMessage("mettre en évidence"), - "image": MessageLookupByLibrary.simpleMessage("l’image"), - "italic": MessageLookupByLibrary.simpleMessage("italique"), - "link": MessageLookupByLibrary.simpleMessage("lien"), - "numberedList": MessageLookupByLibrary.simpleMessage("liste numérotée"), - "quote": MessageLookupByLibrary.simpleMessage("citation"), - "strikethrough": MessageLookupByLibrary.simpleMessage("barré"), - "text": MessageLookupByLibrary.simpleMessage("texte"), - "underline": MessageLookupByLibrary.simpleMessage("souligner") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart deleted file mode 100644 index 07e7302033..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a fr_FR locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'fr_FR'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("Gras"), - "bulletedList": MessageLookupByLibrary.simpleMessage("List à puces"), - "checkbox": MessageLookupByLibrary.simpleMessage("Case à cocher"), - "embedCode": MessageLookupByLibrary.simpleMessage("Incorporer code"), - "heading1": MessageLookupByLibrary.simpleMessage("Titre 1"), - "heading2": MessageLookupByLibrary.simpleMessage("Titre 2"), - "heading3": MessageLookupByLibrary.simpleMessage("Titre 3"), - "highlight": MessageLookupByLibrary.simpleMessage("Surligné"), - "image": MessageLookupByLibrary.simpleMessage("Image"), - "italic": MessageLookupByLibrary.simpleMessage("Italique"), - "link": MessageLookupByLibrary.simpleMessage("Lien"), - "numberedList": MessageLookupByLibrary.simpleMessage("Liste numérotée"), - "quote": MessageLookupByLibrary.simpleMessage("Citation"), - "strikethrough": MessageLookupByLibrary.simpleMessage("Barré"), - "text": MessageLookupByLibrary.simpleMessage("Texte"), - "underline": MessageLookupByLibrary.simpleMessage("Souligné") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_hi-IN.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_hi-IN.dart deleted file mode 100644 index 7f3e29e0af..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_hi-IN.dart +++ /dev/null @@ -1,43 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a hi_IN locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'hi_IN'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("बोल्ड"), - "bulletedList": MessageLookupByLibrary.simpleMessage("बुलेटेड सूची"), - "checkbox": MessageLookupByLibrary.simpleMessage("चेक बॉक्स"), - "embedCode": - MessageLookupByLibrary.simpleMessage("लागु किया गया संहिता"), - "heading1": MessageLookupByLibrary.simpleMessage("शीर्षक 1"), - "heading2": MessageLookupByLibrary.simpleMessage("शीर्षक 2"), - "heading3": MessageLookupByLibrary.simpleMessage("शीर्षक 3"), - "highlight": MessageLookupByLibrary.simpleMessage("प्रमुखता से दिखाना"), - "image": MessageLookupByLibrary.simpleMessage("छवि"), - "italic": MessageLookupByLibrary.simpleMessage("तिरछा"), - "link": MessageLookupByLibrary.simpleMessage("संपर्क"), - "numberedList": MessageLookupByLibrary.simpleMessage("क्रमांकित सूची"), - "quote": MessageLookupByLibrary.simpleMessage("उद्धरण"), - "strikethrough": MessageLookupByLibrary.simpleMessage("स्ट्राइकथ्रू"), - "text": MessageLookupByLibrary.simpleMessage("मूलपाठ"), - "underline": MessageLookupByLibrary.simpleMessage("रेखांकन") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart deleted file mode 100644 index 44a54b5478..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a hu_HU locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'hu_HU'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("bátor"), - "bulletedList": MessageLookupByLibrary.simpleMessage("pontozott lista"), - "checkbox": MessageLookupByLibrary.simpleMessage("jelölőnégyzetet"), - "embedCode": MessageLookupByLibrary.simpleMessage("Beágyazás"), - "heading1": MessageLookupByLibrary.simpleMessage("címsor1"), - "heading2": MessageLookupByLibrary.simpleMessage("címsor2"), - "heading3": MessageLookupByLibrary.simpleMessage("címsor3"), - "highlight": MessageLookupByLibrary.simpleMessage("Kiemel"), - "image": MessageLookupByLibrary.simpleMessage("kép"), - "italic": MessageLookupByLibrary.simpleMessage("dőlt"), - "link": MessageLookupByLibrary.simpleMessage("link"), - "numberedList": MessageLookupByLibrary.simpleMessage("számozottLista"), - "quote": MessageLookupByLibrary.simpleMessage("idézet"), - "strikethrough": MessageLookupByLibrary.simpleMessage("áthúzott"), - "text": MessageLookupByLibrary.simpleMessage("szöveg"), - "underline": MessageLookupByLibrary.simpleMessage("aláhúzás") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart deleted file mode 100644 index cda97336d4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a id_ID locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'id_ID'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("berani"), - "bulletedList": MessageLookupByLibrary.simpleMessage("daftar berpoin"), - "checkbox": MessageLookupByLibrary.simpleMessage("kotak centang"), - "embedCode": MessageLookupByLibrary.simpleMessage("menyematkan Kode"), - "heading1": MessageLookupByLibrary.simpleMessage("pos1"), - "heading2": MessageLookupByLibrary.simpleMessage("pos2"), - "heading3": MessageLookupByLibrary.simpleMessage("pos3"), - "highlight": MessageLookupByLibrary.simpleMessage("menyorot"), - "image": MessageLookupByLibrary.simpleMessage("gambar"), - "italic": MessageLookupByLibrary.simpleMessage("miring"), - "link": MessageLookupByLibrary.simpleMessage("tautan"), - "numberedList": MessageLookupByLibrary.simpleMessage("daftar bernomor"), - "quote": MessageLookupByLibrary.simpleMessage("mengutip"), - "strikethrough": MessageLookupByLibrary.simpleMessage("coret"), - "text": MessageLookupByLibrary.simpleMessage("teks"), - "underline": MessageLookupByLibrary.simpleMessage("menggarisbawahi") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart deleted file mode 100644 index 05ee3e1353..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a it_IT locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'it_IT'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("Grassetto"), - "bulletedList": MessageLookupByLibrary.simpleMessage("Elenco puntato"), - "checkbox": MessageLookupByLibrary.simpleMessage("Casella di spunta"), - "embedCode": MessageLookupByLibrary.simpleMessage("Incorpora codice"), - "heading1": MessageLookupByLibrary.simpleMessage("H1"), - "heading2": MessageLookupByLibrary.simpleMessage("H2"), - "heading3": MessageLookupByLibrary.simpleMessage("H3"), - "highlight": MessageLookupByLibrary.simpleMessage("Evidenzia"), - "image": MessageLookupByLibrary.simpleMessage("Immagine"), - "italic": MessageLookupByLibrary.simpleMessage("Corsivo"), - "link": MessageLookupByLibrary.simpleMessage("Collegamento"), - "numberedList": MessageLookupByLibrary.simpleMessage("Elenco numerato"), - "quote": MessageLookupByLibrary.simpleMessage("Cita"), - "strikethrough": MessageLookupByLibrary.simpleMessage("Barrato"), - "text": MessageLookupByLibrary.simpleMessage("Testo"), - "underline": MessageLookupByLibrary.simpleMessage("Sottolineato") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ja-JP.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ja-JP.dart deleted file mode 100644 index 925acc9668..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ja-JP.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a ja_JP locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'ja_JP'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ml_IN.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ml_IN.dart deleted file mode 100644 index e7378a907e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ml_IN.dart +++ /dev/null @@ -1,45 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a ml_IN locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'ml_IN'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("ബോൾഡ്"), - "bulletedList": - MessageLookupByLibrary.simpleMessage("ബുള്ളറ്റഡ് പട്ടിക"), - "checkbox": MessageLookupByLibrary.simpleMessage("ചെക്ക്ബോക്സ്"), - "embedCode": MessageLookupByLibrary.simpleMessage("എംബെഡഡ് കോഡ്"), - "heading1": MessageLookupByLibrary.simpleMessage("തലക്കെട്ട് 1"), - "heading2": MessageLookupByLibrary.simpleMessage("തലക്കെട്ട് 2"), - "heading3": MessageLookupByLibrary.simpleMessage("തലക്കെട്ട് 3"), - "highlight": - MessageLookupByLibrary.simpleMessage("പ്രമുഖമാക്കിക്കാട്ടുക"), - "image": MessageLookupByLibrary.simpleMessage("ചിത്രം"), - "italic": MessageLookupByLibrary.simpleMessage("ഇറ്റാലിക്"), - "link": MessageLookupByLibrary.simpleMessage("ലിങ്ക്"), - "numberedList": - MessageLookupByLibrary.simpleMessage("അക്കമിട്ട പട്ടിക"), - "quote": MessageLookupByLibrary.simpleMessage("ഉദ്ധരണി"), - "strikethrough": MessageLookupByLibrary.simpleMessage("സ്ട്രൈക്ക്ത്രൂ"), - "text": MessageLookupByLibrary.simpleMessage("വചനം"), - "underline": MessageLookupByLibrary.simpleMessage("അടിവരയിടുക") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_nl-NL.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_nl-NL.dart deleted file mode 100644 index eb096b9a7a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_nl-NL.dart +++ /dev/null @@ -1,43 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a nl_NL locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'nl_NL'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("Vet"), - "bulletedList": - MessageLookupByLibrary.simpleMessage("Opsommingstekens"), - "checkbox": MessageLookupByLibrary.simpleMessage("Selectievakje"), - "embedCode": MessageLookupByLibrary.simpleMessage("Invoegcode"), - "heading1": MessageLookupByLibrary.simpleMessage("H1"), - "heading2": MessageLookupByLibrary.simpleMessage("H2"), - "heading3": MessageLookupByLibrary.simpleMessage("H3"), - "highlight": MessageLookupByLibrary.simpleMessage("Highlight"), - "image": MessageLookupByLibrary.simpleMessage("Afbeelding"), - "italic": MessageLookupByLibrary.simpleMessage("Cursief"), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage("Nummering"), - "quote": MessageLookupByLibrary.simpleMessage("Quote"), - "strikethrough": MessageLookupByLibrary.simpleMessage("Doorhalen"), - "text": MessageLookupByLibrary.simpleMessage("Tekst"), - "underline": MessageLookupByLibrary.simpleMessage("Onderstrepen") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pl-PL.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pl-PL.dart deleted file mode 100644 index 30a9cc38b2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pl-PL.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a pl_PL locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'pl_PL'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart deleted file mode 100644 index 22c53407ac..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart +++ /dev/null @@ -1,43 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a pt_BR locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'pt_BR'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("Negrito"), - "bulletedList": - MessageLookupByLibrary.simpleMessage("Lista de marcadores"), - "checkbox": MessageLookupByLibrary.simpleMessage("Caixa de seleção"), - "embedCode": MessageLookupByLibrary.simpleMessage("Código incorporado"), - "heading1": MessageLookupByLibrary.simpleMessage("H1"), - "heading2": MessageLookupByLibrary.simpleMessage("H2"), - "heading3": MessageLookupByLibrary.simpleMessage("H3"), - "highlight": MessageLookupByLibrary.simpleMessage("Destacar"), - "image": MessageLookupByLibrary.simpleMessage("Imagem"), - "italic": MessageLookupByLibrary.simpleMessage("Itálico"), - "link": MessageLookupByLibrary.simpleMessage("Link"), - "numberedList": MessageLookupByLibrary.simpleMessage("Lista numerada"), - "quote": MessageLookupByLibrary.simpleMessage("Citar"), - "strikethrough": MessageLookupByLibrary.simpleMessage("Rasurar"), - "text": MessageLookupByLibrary.simpleMessage("Texto"), - "underline": MessageLookupByLibrary.simpleMessage("Sublinhar") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart deleted file mode 100644 index d2d2781580..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart +++ /dev/null @@ -1,43 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a pt_PT locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'pt_PT'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("negrito"), - "bulletedList": - MessageLookupByLibrary.simpleMessage("lista com marcadores"), - "checkbox": MessageLookupByLibrary.simpleMessage("caixa de seleção"), - "embedCode": MessageLookupByLibrary.simpleMessage("Código embutido"), - "heading1": MessageLookupByLibrary.simpleMessage("Cabeçallho 1"), - "heading2": MessageLookupByLibrary.simpleMessage("Cabeçallho 2"), - "heading3": MessageLookupByLibrary.simpleMessage("Cabeçallho 3"), - "highlight": MessageLookupByLibrary.simpleMessage("realçar"), - "image": MessageLookupByLibrary.simpleMessage("imagem"), - "italic": MessageLookupByLibrary.simpleMessage("itálico"), - "link": MessageLookupByLibrary.simpleMessage("link"), - "numberedList": MessageLookupByLibrary.simpleMessage("lista numerada"), - "quote": MessageLookupByLibrary.simpleMessage("citar"), - "strikethrough": MessageLookupByLibrary.simpleMessage("tachado"), - "text": MessageLookupByLibrary.simpleMessage("texto"), - "underline": MessageLookupByLibrary.simpleMessage("sublinhado") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ru-RU.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ru-RU.dart deleted file mode 100644 index 21ae9f6a0e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_ru-RU.dart +++ /dev/null @@ -1,44 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a ru_RU locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'ru_RU'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage("смелый"), - "bulletedList": - MessageLookupByLibrary.simpleMessage("маркированный список"), - "checkbox": MessageLookupByLibrary.simpleMessage("флажок"), - "embedCode": MessageLookupByLibrary.simpleMessage("код для вставки"), - "heading1": MessageLookupByLibrary.simpleMessage("заголовок1"), - "heading2": MessageLookupByLibrary.simpleMessage("заголовок2"), - "heading3": MessageLookupByLibrary.simpleMessage("заголовок3"), - "highlight": MessageLookupByLibrary.simpleMessage("выделять"), - "image": MessageLookupByLibrary.simpleMessage("изображение"), - "italic": MessageLookupByLibrary.simpleMessage("курсив"), - "link": MessageLookupByLibrary.simpleMessage("ссылка на сайт"), - "numberedList": - MessageLookupByLibrary.simpleMessage("нумерованный список"), - "quote": MessageLookupByLibrary.simpleMessage("цитировать"), - "strikethrough": MessageLookupByLibrary.simpleMessage("зачеркнутый"), - "text": MessageLookupByLibrary.simpleMessage("текст"), - "underline": MessageLookupByLibrary.simpleMessage("подчеркнуть") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_tr-TR.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_tr-TR.dart deleted file mode 100644 index 50ba27e790..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_tr-TR.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a tr_TR locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'tr_TR'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-CN.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-CN.dart deleted file mode 100644 index 21a3b9d7a2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-CN.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a zh_CN locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'zh_CN'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-TW.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-TW.dart deleted file mode 100644 index 8fbe4a836b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-TW.dart +++ /dev/null @@ -1,42 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a zh_TW locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'zh_TW'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") - }; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/l10n.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/l10n.dart deleted file mode 100644 index fdd0bbb60c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/l10n/l10n.dart +++ /dev/null @@ -1,652 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'intl/messages_all.dart'; - -// ************************************************************************** -// Generator: Flutter Intl IDE plugin -// Made by Localizely -// ************************************************************************** - -// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars -// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each -// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes - -class AppFlowyEditorLocalizations { - AppFlowyEditorLocalizations(); - - static AppFlowyEditorLocalizations? _current; - - static AppFlowyEditorLocalizations get current { - assert(_current != null, - 'No instance of AppFlowyEditorLocalizations was loaded. Try to initialize the AppFlowyEditorLocalizations delegate before accessing AppFlowyEditorLocalizations.current.'); - return _current!; - } - - static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); - - static Future load(Locale locale) { - final name = (locale.countryCode?.isEmpty ?? false) - ? locale.languageCode - : locale.toString(); - final localeName = Intl.canonicalizedLocale(name); - return initializeMessages(localeName).then((_) { - Intl.defaultLocale = localeName; - final instance = AppFlowyEditorLocalizations(); - AppFlowyEditorLocalizations._current = instance; - - return instance; - }); - } - - static AppFlowyEditorLocalizations of(BuildContext context) { - final instance = AppFlowyEditorLocalizations.maybeOf(context); - assert(instance != null, - 'No instance of AppFlowyEditorLocalizations present in the widget tree. Did you add AppFlowyEditorLocalizations.delegate in localizationsDelegates?'); - return instance!; - } - - static AppFlowyEditorLocalizations? maybeOf(BuildContext context) { - return Localizations.of( - context, AppFlowyEditorLocalizations); - } - - /// `Bold` - String get bold { - return Intl.message( - 'Bold', - name: 'bold', - desc: '', - args: [], - ); - } - - /// `Bulleted List` - String get bulletedList { - return Intl.message( - 'Bulleted List', - name: 'bulletedList', - desc: '', - args: [], - ); - } - - /// `Checkbox` - String get checkbox { - return Intl.message( - 'Checkbox', - name: 'checkbox', - desc: '', - args: [], - ); - } - - /// `Embed Code` - String get embedCode { - return Intl.message( - 'Embed Code', - name: 'embedCode', - desc: '', - args: [], - ); - } - - /// `H1` - String get heading1 { - return Intl.message( - 'H1', - name: 'heading1', - desc: '', - args: [], - ); - } - - /// `H2` - String get heading2 { - return Intl.message( - 'H2', - name: 'heading2', - desc: '', - args: [], - ); - } - - /// `H3` - String get heading3 { - return Intl.message( - 'H3', - name: 'heading3', - desc: '', - args: [], - ); - } - - /// `Highlight` - String get highlight { - return Intl.message( - 'Highlight', - name: 'highlight', - desc: '', - args: [], - ); - } - - /// `Color` - String get color { - return Intl.message( - 'Color', - name: 'color', - desc: '', - args: [], - ); - } - - /// `Image` - String get image { - return Intl.message( - 'Image', - name: 'image', - desc: '', - args: [], - ); - } - - /// `Italic` - String get italic { - return Intl.message( - 'Italic', - name: 'italic', - desc: '', - args: [], - ); - } - - /// `Link` - String get link { - return Intl.message( - 'Link', - name: 'link', - desc: '', - args: [], - ); - } - - /// `Numbered List` - String get numberedList { - return Intl.message( - 'Numbered List', - name: 'numberedList', - desc: '', - args: [], - ); - } - - /// `Quote` - String get quote { - return Intl.message( - 'Quote', - name: 'quote', - desc: '', - args: [], - ); - } - - /// `Strikethrough` - String get strikethrough { - return Intl.message( - 'Strikethrough', - name: 'strikethrough', - desc: '', - args: [], - ); - } - - /// `Text` - String get text { - return Intl.message( - 'Text', - name: 'text', - desc: '', - args: [], - ); - } - - /// `Underline` - String get underline { - return Intl.message( - 'Underline', - name: 'underline', - desc: '', - args: [], - ); - } - - /// `Default` - String get fontColorDefault { - return Intl.message( - 'Default', - name: 'fontColorDefault', - desc: '', - args: [], - ); - } - - /// `Gray` - String get fontColorGray { - return Intl.message( - 'Gray', - name: 'fontColorGray', - desc: '', - args: [], - ); - } - - /// `Brown` - String get fontColorBrown { - return Intl.message( - 'Brown', - name: 'fontColorBrown', - desc: '', - args: [], - ); - } - - /// `Orange` - String get fontColorOrange { - return Intl.message( - 'Orange', - name: 'fontColorOrange', - desc: '', - args: [], - ); - } - - /// `Yellow` - String get fontColorYellow { - return Intl.message( - 'Yellow', - name: 'fontColorYellow', - desc: '', - args: [], - ); - } - - /// `Green` - String get fontColorGreen { - return Intl.message( - 'Green', - name: 'fontColorGreen', - desc: '', - args: [], - ); - } - - /// `Blue` - String get fontColorBlue { - return Intl.message( - 'Blue', - name: 'fontColorBlue', - desc: '', - args: [], - ); - } - - /// `Purple` - String get fontColorPurple { - return Intl.message( - 'Purple', - name: 'fontColorPurple', - desc: '', - args: [], - ); - } - - /// `Pink` - String get fontColorPink { - return Intl.message( - 'Pink', - name: 'fontColorPink', - desc: '', - args: [], - ); - } - - /// `Red` - String get fontColorRed { - return Intl.message( - 'Red', - name: 'fontColorRed', - desc: '', - args: [], - ); - } - - /// `Default background` - String get backgroundColorDefault { - return Intl.message( - 'Default background', - name: 'backgroundColorDefault', - desc: '', - args: [], - ); - } - - /// `Gray background` - String get backgroundColorGray { - return Intl.message( - 'Gray background', - name: 'backgroundColorGray', - desc: '', - args: [], - ); - } - - /// `Brown background` - String get backgroundColorBrown { - return Intl.message( - 'Brown background', - name: 'backgroundColorBrown', - desc: '', - args: [], - ); - } - - /// `Orange background` - String get backgroundColorOrange { - return Intl.message( - 'Orange background', - name: 'backgroundColorOrange', - desc: '', - args: [], - ); - } - - /// `Yellow background` - String get backgroundColorYellow { - return Intl.message( - 'Yellow background', - name: 'backgroundColorYellow', - desc: '', - args: [], - ); - } - - /// `Green background` - String get backgroundColorGreen { - return Intl.message( - 'Green background', - name: 'backgroundColorGreen', - desc: '', - args: [], - ); - } - - /// `Blue background` - String get backgroundColorBlue { - return Intl.message( - 'Blue background', - name: 'backgroundColorBlue', - desc: '', - args: [], - ); - } - - /// `Purple background` - String get backgroundColorPurple { - return Intl.message( - 'Purple background', - name: 'backgroundColorPurple', - desc: '', - args: [], - ); - } - - /// `Pink background` - String get backgroundColorPink { - return Intl.message( - 'Pink background', - name: 'backgroundColorPink', - desc: '', - args: [], - ); - } - - /// `Red background` - String get backgroundColorRed { - return Intl.message( - 'Red background', - name: 'backgroundColorRed', - desc: '', - args: [], - ); - } - - /// `Tint 1` - String get tint1 { - return Intl.message( - 'Tint 1', - name: 'tint1', - desc: '', - args: [], - ); - } - - /// `Tint 2` - String get tint2 { - return Intl.message( - 'Tint 2', - name: 'tint2', - desc: '', - args: [], - ); - } - - /// `Tint 3` - String get tint3 { - return Intl.message( - 'Tint 3', - name: 'tint3', - desc: '', - args: [], - ); - } - - /// `Tint 4` - String get tint4 { - return Intl.message( - 'Tint 4', - name: 'tint4', - desc: '', - args: [], - ); - } - - /// `Tint 5` - String get tint5 { - return Intl.message( - 'Tint 5', - name: 'tint5', - desc: '', - args: [], - ); - } - - /// `Tint 6` - String get tint6 { - return Intl.message( - 'Tint 6', - name: 'tint6', - desc: '', - args: [], - ); - } - - /// `Tint 7` - String get tint7 { - return Intl.message( - 'Tint 7', - name: 'tint7', - desc: '', - args: [], - ); - } - - /// `Tint 8` - String get tint8 { - return Intl.message( - 'Tint 8', - name: 'tint8', - desc: '', - args: [], - ); - } - - /// `Tint 9` - String get tint9 { - return Intl.message( - 'Tint 9', - name: 'tint9', - desc: '', - args: [], - ); - } - - /// `Purple` - String get lightLightTint1 { - return Intl.message( - 'Purple', - name: 'lightLightTint1', - desc: '', - args: [], - ); - } - - /// `Pink` - String get lightLightTint2 { - return Intl.message( - 'Pink', - name: 'lightLightTint2', - desc: '', - args: [], - ); - } - - /// `Light Pink` - String get lightLightTint3 { - return Intl.message( - 'Light Pink', - name: 'lightLightTint3', - desc: '', - args: [], - ); - } - - /// `Orange` - String get lightLightTint4 { - return Intl.message( - 'Orange', - name: 'lightLightTint4', - desc: '', - args: [], - ); - } - - /// `Yellow` - String get lightLightTint5 { - return Intl.message( - 'Yellow', - name: 'lightLightTint5', - desc: '', - args: [], - ); - } - - /// `Lime` - String get lightLightTint6 { - return Intl.message( - 'Lime', - name: 'lightLightTint6', - desc: '', - args: [], - ); - } - - /// `Green` - String get lightLightTint7 { - return Intl.message( - 'Green', - name: 'lightLightTint7', - desc: '', - args: [], - ); - } - - /// `Aqua` - String get lightLightTint8 { - return Intl.message( - 'Aqua', - name: 'lightLightTint8', - desc: '', - args: [], - ); - } - - /// `Blue` - String get lightLightTint9 { - return Intl.message( - 'Blue', - name: 'lightLightTint9', - desc: '', - args: [], - ); - } -} - -class AppLocalizationDelegate - extends LocalizationsDelegate { - const AppLocalizationDelegate(); - - List get supportedLocales { - return const [ - Locale.fromSubtags(languageCode: 'en'), - Locale.fromSubtags(languageCode: 'bn', countryCode: 'BN'), - Locale.fromSubtags(languageCode: 'ca'), - Locale.fromSubtags(languageCode: 'cs', countryCode: 'CZ'), - Locale.fromSubtags(languageCode: 'de', countryCode: 'DE'), - Locale.fromSubtags(languageCode: 'es', countryCode: 'VE'), - Locale.fromSubtags(languageCode: 'fr', countryCode: 'CA'), - Locale.fromSubtags(languageCode: 'fr', countryCode: 'FR'), - Locale.fromSubtags(languageCode: 'hi', countryCode: 'IN'), - Locale.fromSubtags(languageCode: 'hu', countryCode: 'HU'), - Locale.fromSubtags(languageCode: 'id', countryCode: 'ID'), - Locale.fromSubtags(languageCode: 'it', countryCode: 'IT'), - Locale.fromSubtags(languageCode: 'ja', countryCode: 'JP'), - Locale.fromSubtags(languageCode: 'ml', countryCode: 'IN'), - Locale.fromSubtags(languageCode: 'nl', countryCode: 'NL'), - Locale.fromSubtags(languageCode: 'pl', countryCode: 'PL'), - Locale.fromSubtags(languageCode: 'pt', countryCode: 'BR'), - Locale.fromSubtags(languageCode: 'pt', countryCode: 'PT'), - Locale.fromSubtags(languageCode: 'ru', countryCode: 'RU'), - Locale.fromSubtags(languageCode: 'tr', countryCode: 'TR'), - Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'), - Locale.fromSubtags(languageCode: 'zh', countryCode: 'TW'), - ]; - } - - @override - bool isSupported(Locale locale) => _isSupported(locale); - @override - Future load(Locale locale) => - AppFlowyEditorLocalizations.load(locale); - @override - bool shouldReload(AppLocalizationDelegate old) => false; - - bool _isSupported(Locale locale) { - for (var supportedLocale in supportedLocales) { - if (supportedLocale.languageCode == locale.languageCode) { - return true; - } - } - return false; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/decoder/delta_markdown_decoder.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/decoder/delta_markdown_decoder.dart deleted file mode 100644 index e015546989..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/decoder/delta_markdown_decoder.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; -import 'package:markdown/markdown.dart' as md; - -class DeltaMarkdownDecoder extends Converter - with md.NodeVisitor { - final _delta = Delta(); - final Attributes _attributes = {}; - - @override - Delta convert(String input) { - final document = - md.Document(extensionSet: md.ExtensionSet.gitHubWeb).parseInline(input); - for (final node in document) { - node.accept(this); - } - return _delta; - } - - @override - void visitElementAfter(md.Element element) { - _removeAttributeKey(element); - } - - @override - bool visitElementBefore(md.Element element) { - _addAttributeKey(element); - return true; - } - - @override - void visitText(md.Text text) { - _delta.add(TextInsert(text.text, attributes: {..._attributes})); - } - - void _addAttributeKey(md.Element element) { - if (element.tag == 'strong') { - _attributes[BuiltInAttributeKey.bold] = true; - } else if (element.tag == 'em') { - _attributes[BuiltInAttributeKey.italic] = true; - } else if (element.tag == 'code') { - _attributes[BuiltInAttributeKey.code] = true; - } else if (element.tag == 'del') { - _attributes[BuiltInAttributeKey.strikethrough] = true; - } else if (element.tag == 'a') { - _attributes[BuiltInAttributeKey.href] = element.attributes['href']; - } - } - - void _removeAttributeKey(md.Element element) { - if (element.tag == 'strong') { - _attributes.remove(BuiltInAttributeKey.bold); - } else if (element.tag == 'em') { - _attributes.remove(BuiltInAttributeKey.italic); - } else if (element.tag == 'code') { - _attributes.remove(BuiltInAttributeKey.code); - } else if (element.tag == 'del') { - _attributes.remove(BuiltInAttributeKey.strikethrough); - } else if (element.tag == 'a') { - _attributes.remove(BuiltInAttributeKey.href); - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/decoder/document_markdown_decoder.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/decoder/document_markdown_decoder.dart deleted file mode 100644 index fa12159106..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/decoder/document_markdown_decoder.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/appflowy_editor.dart'; - -class DocumentMarkdownDecoder extends Converter { - @override - Document convert(String input) { - final lines = input.split('\n'); - final document = Document.empty(); - - var i = 0; - for (final line in lines) { - document.insert([i++], [_convertLineToNode(line)]); - } - - return document; - } - - Node _convertLineToNode(String text) { - final decoder = DeltaMarkdownDecoder(); - // Heading Style - if (text.startsWith('### ')) { - return TextNode( - delta: decoder.convert(text.substring(4)), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h3, - }, - ); - } else if (text.startsWith('## ')) { - return TextNode( - delta: decoder.convert(text.substring(3)), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h2, - }, - ); - } else if (text.startsWith('# ')) { - return TextNode( - delta: decoder.convert(text.substring(2)), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h1, - }, - ); - } else if (text.startsWith('- [ ] ')) { - return TextNode( - delta: decoder.convert(text.substring(6)), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - ); - } else if (text.startsWith('- [x] ')) { - return TextNode( - delta: decoder.convert(text.substring(6)), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: true, - }, - ); - } else if (text.startsWith('> ')) { - return TextNode( - delta: decoder.convert(text.substring(2)), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }, - ); - } else if (text.startsWith('- ') || text.startsWith('* ')) { - return TextNode( - delta: decoder.convert(text.substring(2)), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }, - ); - } else if (text.startsWith('> ')) { - return TextNode( - delta: decoder.convert(text.substring(2)), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }, - ); - } else if (text.isNotEmpty && RegExp(r'^-*').stringMatch(text) == text) { - return Node(type: 'divider'); - } - - if (text.isNotEmpty) { - return TextNode(delta: decoder.convert(text)); - } - - return TextNode(delta: Delta()); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/document_markdown.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/document_markdown.dart deleted file mode 100644 index b47c17fe73..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/document_markdown.dart +++ /dev/null @@ -1,49 +0,0 @@ -library delta_markdown; - -import 'dart:convert'; - -import 'package:appflowy_editor/src/core/document/document.dart'; -import 'package:appflowy_editor/src/plugins/markdown/decoder/document_markdown_decoder.dart'; -import 'package:appflowy_editor/src/plugins/markdown/encoder/document_markdown_encoder.dart'; -import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/image_node_parser.dart'; -import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart'; -import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/text_node_parser.dart'; - -/// Converts a markdown to [Document]. -/// -/// [customParsers] is a list of custom parsers that will be used to parse the markdown. -Document markdownToDocument( - String markdown, { - List customParsers = const [], -}) { - return const AppFlowyEditorMarkdownCodec().decode(markdown); -} - -/// Converts a [Document] to markdown. -/// -/// [customParsers] is a list of custom parsers that will be used to parse the markdown. -String documentToMarkdown(Document document, - {List customParsers = const []}) { - return AppFlowyEditorMarkdownCodec(encodeParsers: [ - ...customParsers, - const TextNodeParser(), - const ImageNodeParser(), - ]).encode(document); -} - -class AppFlowyEditorMarkdownCodec extends Codec { - const AppFlowyEditorMarkdownCodec({ - this.encodeParsers = const [], - }); - - final List encodeParsers; - - // TODO: Add support for custom parsers - @override - Converter get decoder => DocumentMarkdownDecoder(); - - @override - Converter get encoder => DocumentMarkdownEncoder( - parsers: encodeParsers, - ); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/delta_markdown_encoder.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/delta_markdown_encoder.dart deleted file mode 100644 index 5c8bd9ebf9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/delta_markdown_encoder.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/appflowy_editor.dart'; - -/// A [Delta] encoder that encodes a [Delta] to Markdown. -/// -/// Only support inline styles, like bold, italic, underline, strike, code. -class DeltaMarkdownEncoder extends Converter { - @override - String convert(Delta input) { - final buffer = StringBuffer(); - final iterator = input.iterator; - while (iterator.moveNext()) { - final op = iterator.current; - if (op is TextInsert) { - final attributes = op.attributes; - if (attributes != null) { - buffer.write(_prefixSyntax(attributes)); - buffer.write(op.text); - buffer.write(_suffixSyntax(attributes)); - } else { - buffer.write(op.text); - } - } - } - return buffer.toString(); - } - - String _prefixSyntax(Attributes attributes) { - var syntax = ''; - - if (attributes[BuiltInAttributeKey.bold] == true && - attributes[BuiltInAttributeKey.italic] == true) { - syntax += '***'; - } else if (attributes[BuiltInAttributeKey.bold] == true) { - syntax += '**'; - } else if (attributes[BuiltInAttributeKey.italic] == true) { - syntax += '_'; - } - - if (attributes[BuiltInAttributeKey.strikethrough] == true) { - syntax += '~~'; - } - if (attributes[BuiltInAttributeKey.underline] == true) { - syntax += ''; - } - if (attributes[BuiltInAttributeKey.code] == true) { - syntax += '`'; - } - - if (attributes[BuiltInAttributeKey.href] != null) { - syntax += '['; - } - - return syntax; - } - - String _suffixSyntax(Attributes attributes) { - var syntax = ''; - - if (attributes[BuiltInAttributeKey.href] != null) { - syntax += '](${attributes[BuiltInAttributeKey.href]})'; - } - - if (attributes[BuiltInAttributeKey.code] == true) { - syntax += '`'; - } - - if (attributes[BuiltInAttributeKey.underline] == true) { - syntax += ''; - } - - if (attributes[BuiltInAttributeKey.strikethrough] == true) { - syntax += '~~'; - } - - if (attributes[BuiltInAttributeKey.bold] == true && - attributes[BuiltInAttributeKey.italic] == true) { - syntax += '***'; - } else if (attributes[BuiltInAttributeKey.bold] == true) { - syntax += '**'; - } else if (attributes[BuiltInAttributeKey.italic] == true) { - syntax += '_'; - } - - return syntax; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/document_markdown_encoder.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/document_markdown_encoder.dart deleted file mode 100644 index 1963a9f636..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/document_markdown_encoder.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/src/core/document/document.dart'; -import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart'; - -class DocumentMarkdownEncoder extends Converter { - DocumentMarkdownEncoder({ - this.parsers = const [], - }); - - final List parsers; - - @override - String convert(Document input) { - final buffer = StringBuffer(); - for (final node in input.root.children) { - NodeParser? parser = - parsers.firstWhereOrNull((element) => element.id == node.type); - if (parser != null) { - buffer.write(parser.transform(node)); - } - } - return buffer.toString(); - } -} - -extension IterableExtension on Iterable { - T? firstWhereOrNull(bool Function(T element) test) { - for (var element in this) { - if (test(element)) return element; - } - return null; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/image_node_parser.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/image_node_parser.dart deleted file mode 100644 index 5db9f1b558..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/image_node_parser.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart'; - -class ImageNodeParser extends NodeParser { - const ImageNodeParser(); - - @override - String get id => 'image'; - - @override - String transform(Node node) { - return '![](${node.attributes['image_src']})'; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/node_parser.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/node_parser.dart deleted file mode 100644 index 9cbdabfbb9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/node_parser.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; - -abstract class NodeParser { - const NodeParser(); - - String get id; - String transform(Node node); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/text_node_parser.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/text_node_parser.dart deleted file mode 100644 index f6532c91d1..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/markdown/encoder/parser/text_node_parser.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; -import 'package:appflowy_editor/src/plugins/markdown/encoder/delta_markdown_encoder.dart'; -import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart'; - -class TextNodeParser extends NodeParser { - const TextNodeParser(); - - @override - String get id => 'text'; - - @override - String transform(Node node) { - assert(node is TextNode); - final textNode = node as TextNode; - final markdown = DeltaMarkdownEncoder().convert(textNode.delta); - final attributes = textNode.attributes; - var result = markdown; - var suffix = '\n'; - if (attributes.isNotEmpty && - attributes.containsKey(BuiltInAttributeKey.subtype)) { - final subtype = attributes[BuiltInAttributeKey.subtype]; - if (node.next == null) { - suffix = ''; - } - if (subtype == 'heading') { - final heading = attributes[BuiltInAttributeKey.heading]; - if (heading == 'h1') { - result = '# $markdown'; - } else if (heading == 'h2') { - result = '## $markdown'; - } else if (heading == 'h3') { - result = '### $markdown'; - } else if (heading == 'h4') { - result = '#### $markdown'; - } else if (heading == 'h5') { - result = '##### $markdown'; - } else if (heading == 'h6') { - result = '###### $markdown'; - } - } else if (subtype == 'quote') { - result = '> $markdown'; - } else if (subtype == 'code_block') { - result = '```\n$markdown\n```'; - } else if (subtype == 'bulleted-list') { - result = '* $markdown'; - } else if (subtype == 'number-list') { - final number = attributes['number']; - result = '$number. $markdown'; - } else if (subtype == 'checkbox') { - if (attributes[BuiltInAttributeKey.checkbox] == true) { - result = '- [x] $markdown'; - } else { - result = '- [ ] $markdown'; - } - } - } else { - if (node.next == null) { - suffix = ''; - } - } - return '$result$suffix'; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/quill_delta/delta_document_encoder.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/quill_delta/delta_document_encoder.dart deleted file mode 100644 index e2b71684d7..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/plugins/quill_delta/delta_document_encoder.dart +++ /dev/null @@ -1,232 +0,0 @@ -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:appflowy_editor/src/core/document/document.dart'; -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; -import 'package:flutter/material.dart'; - -class DeltaDocumentConvert { - DeltaDocumentConvert(); - - var _number = 1; - final Map> _bulletedList = {}; - - Document convertFromJSON(List json) { - final delta = Delta.fromJson(json); - return convertFromDelta(delta); - } - - Document convertFromDelta(Delta delta) { - final iter = delta.iterator; - - final document = Document.empty(); - TextNode textNode = TextNode(delta: Delta()); - int path = 0; - - while (iter.moveNext()) { - final op = iter.current; - if (op is TextInsert) { - if (op.text != '\n') { - // Attributes associated with a newline character describes formatting for that line. - final texts = op.text.split('\n'); - if (texts.length > 1) { - textNode.delta.insert(texts[0]); - document.insert([path++], [textNode]); - textNode = TextNode(delta: Delta()..insert(texts[1])); - } else { - _applyStyle(textNode, op.text, op.attributes); - } - } else { - if (!_containNumberListStyle(op.attributes)) { - _number = 1; - } - _applyListStyle(textNode, op.attributes); - _applyHeaderStyle(textNode, op.attributes); - _applyIndent(textNode, op.attributes); - _applyBlockquote(textNode, op.attributes); - // _applyCodeBlock(textNode, op.attributes); - - if (_containIndentBulletedListStyle(op.attributes)) { - final level = _indentLevel(op.attributes); - final path = [ - ..._bulletedList[level - 1]!.last.path, - _bulletedList[level]!.length - 1, - ]; - document.insert(path, [textNode]); - } else { - document.insert([path++], [textNode]); - } - textNode = TextNode(delta: Delta()); - } - } else { - assert(false, 'op must be TextInsert'); - } - } - - return document; - } - - void _applyStyle(TextNode textNode, String text, Map? attributes) { - Attributes attrs = {}; - - if (_containsStyle(attributes, 'strike')) { - attrs[BuiltInAttributeKey.strikethrough] = true; - } - if (_containsStyle(attributes, 'underline')) { - attrs[BuiltInAttributeKey.underline] = true; - } - if (_containsStyle(attributes, 'bold')) { - attrs[BuiltInAttributeKey.bold] = true; - } - if (_containsStyle(attributes, 'italic')) { - attrs[BuiltInAttributeKey.italic] = true; - } - final link = attributes?['link'] as String?; - if (link != null) { - attrs[BuiltInAttributeKey.href] = link; - } - final color = attributes?['color'] as String?; - final colorHex = _convertColorToHexString(color); - if (colorHex != null) { - attrs[BuiltInAttributeKey.color] = colorHex; - } - final backgroundColor = attributes?['background'] as String?; - final backgroundHex = _convertColorToHexString(backgroundColor); - if (backgroundHex != null) { - attrs[BuiltInAttributeKey.backgroundColor] = backgroundHex; - } - - textNode.delta.insert(text, attributes: attrs); - } - - bool _containsStyle(Map? attributes, String key) { - final value = attributes?[key] as bool?; - return value == true; - } - - String? _convertColorToHexString(String? color) { - if (color == null) { - return null; - } - if (color.startsWith('#')) { - return '0xFF${color.substring(1)}'; - } else if (color.startsWith("rgba")) { - List rgbaList = color.substring(5, color.length - 1).split(','); - return Color.fromRGBO( - int.parse(rgbaList[0]), - int.parse(rgbaList[1]), - int.parse(rgbaList[2]), - double.parse(rgbaList[3]), - ).toHex(); - } - return null; - } - - // convert bullet-list, number-list, check-list to appflowy style list. - void _applyListStyle(TextNode textNode, Map? attributes) { - final indent = attributes?['indent'] as int?; - final list = attributes?['list'] as String?; - if (list != null) { - switch (list) { - case 'bullet': - textNode.updateAttributes({ - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }); - if (indent != null) { - _bulletedList[indent] ??= []; - _bulletedList[indent]?.add(textNode); - } else { - _bulletedList.clear(); - _bulletedList[0] ??= []; - _bulletedList[0]?.add(textNode); - } - break; - case 'ordered': - textNode.updateAttributes({ - BuiltInAttributeKey.subtype: BuiltInAttributeKey.numberList, - BuiltInAttributeKey.number: _number++, - }); - break; - case 'checked': - textNode.updateAttributes({ - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: true, - }); - break; - case 'unchecked': - textNode.updateAttributes({ - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }); - break; - } - } - } - - bool _containNumberListStyle(Map? attributes) { - final list = attributes?['list'] as String?; - return list == 'ordered'; - } - - bool _containIndentBulletedListStyle(Map? attributes) { - final list = attributes?['list'] as String?; - final indent = attributes?['indent'] as int?; - return list == 'bullet' && indent != null; - } - - int _indentLevel(Map? attributes) { - final indent = attributes?['indent'] as int?; - return indent ?? 1; - } - - // convert header to appflowy style heading - void _applyHeaderStyle(TextNode textNode, Map? attributes) { - final header = attributes?['header'] as int?; - if (header != null) { - textNode.updateAttributes({ - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: 'h$header', - }); - } - } - - // convert indent to tab - void _applyIndent(TextNode textNode, Map? attributes) { - final indent = attributes?['indent'] as int?; - final list = attributes?['list'] as String?; - if (indent != null && list == null) { - textNode.delta = textNode.delta.compose( - Delta() - ..retain(0) - ..insert(' ' * indent), - ); - } - } - - /* - // convert code-block to appflowy style code - void _applyCodeBlock(TextNode textNode, Map? attributes) { - final codeBlock = attributes?['code-block'] as bool?; - if (codeBlock != null) { - textNode.updateAttributes({ - BuiltInAttributeKey.subtype: 'code_block', - }); - } - } - */ - - void _applyBlockquote(TextNode textNode, Map? attributes) { - final blockquote = attributes?['blockquote'] as bool?; - if (blockquote != null) { - textNode.updateAttributes({ - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }); - } - } -} - -extension on Color { - String toHex() { - return '0x${value.toRadixString(16)}'; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/action_menu/action_menu.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/action_menu/action_menu.dart deleted file mode 100644 index 1e242094f8..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/action_menu/action_menu.dart +++ /dev/null @@ -1,180 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/render/action_menu/action_menu_item.dart'; -import 'package:appflowy_editor/src/render/style/editor_style.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -/// [ActionProvider] is an optional mixin to define the actions of a node widget. -mixin ActionProvider on NodeWidgetBuilder { - List actions(NodeWidgetContext context); -} - -class ActionMenuArenaMember { - final ActionMenuState state; - final VoidCallback listener; - - const ActionMenuArenaMember({required this.state, required this.listener}); -} - -/// Decides which action menu is visible. -/// The menu with the greatest [Node.path] wins. -class ActionMenuArena { - final Map _members = {}; - final Set _visible = {}; - - ActionMenuArena._singleton(); - static final instance = ActionMenuArena._singleton(); - - void add(ActionMenuState menuState) { - final member = ActionMenuArenaMember( - state: menuState, - listener: () { - final len = _visible.length; - if (menuState.isHover || menuState.isPinned) { - _visible.add(menuState.path); - } else { - _visible.remove(menuState.path); - } - if (len != _visible.length) { - _notifyAllVisible(); - } - }, - ); - menuState.addListener(member.listener); - _members[menuState.path] = member; - } - - void _notifyAllVisible() { - for (var path in _visible) { - _members[path]?.state.notify(); - } - } - - void remove(ActionMenuState menuState) { - final member = _members.remove(menuState.path); - if (member != null) { - menuState.removeListener(member.listener); - _visible.remove(menuState.path); - } - } - - bool isVisible(Path path) { - var sorted = _visible.toList() - ..sort( - (a, b) => a <= b ? 1 : -1, - ); - return sorted.isNotEmpty && path == sorted.first; - } -} - -/// Used to manage the state of each [ActionMenuOverlay]. -class ActionMenuState extends ChangeNotifier { - final Path path; - - ActionMenuState(this.path) { - ActionMenuArena.instance.add(this); - } - - @override - void dispose() { - ActionMenuArena.instance.remove(this); - super.dispose(); - } - - bool _isHover = false; - bool _isPinned = false; - - bool get isPinned => _isPinned; - bool get isHover => _isHover; - bool get isVisible => ActionMenuArena.instance.isVisible(path); - - set isPinned(bool value) { - if (_isPinned == value) { - return; - } - _isPinned = value; - notifyListeners(); - } - - set isHover(bool value) { - if (_isHover == value) { - return; - } - _isHover = value; - notifyListeners(); - } - - void notify() { - notifyListeners(); - } -} - -/// The default widget to render an action menu -class ActionMenuWidget extends StatelessWidget { - final List items; - - const ActionMenuWidget({super.key, required this.items}); - - @override - Widget build(BuildContext context) { - final editorStyle = EditorStyle.of(context); - - return Card( - color: editorStyle?.selectionMenuBackgroundColor, - elevation: 3.0, - child: Row( - mainAxisSize: MainAxisSize.min, - children: items.map((item) { - return ActionMenuItemWidget( - item: item, - ); - }).toList(), - ), - ); - } -} - -class ActionMenuOverlay extends StatelessWidget { - final Widget child; - final List items; - final Positioned Function(BuildContext context, List items)? - customActionMenuBuilder; - - const ActionMenuOverlay({ - super.key, - required this.items, - required this.child, - this.customActionMenuBuilder, - }); - - @override - Widget build(BuildContext context) { - final menuState = Provider.of(context); - - return MouseRegion( - onEnter: (_) { - menuState.isHover = true; - }, - onExit: (_) { - menuState.isHover = false; - }, - onHover: (_) { - menuState.isHover = true; - }, - child: Stack( - children: [ - child, - if (menuState.isVisible) _buildMenu(context), - ], - ), - ); - } - - Positioned _buildMenu(BuildContext context) { - return customActionMenuBuilder != null - ? customActionMenuBuilder!(context, items) - : Positioned(top: 5, right: 5, child: ActionMenuWidget(items: items)); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/action_menu/action_menu_item.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/action_menu/action_menu_item.dart deleted file mode 100644 index 5129b83141..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/action_menu/action_menu_item.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/infra/flowy_svg.dart'; -import 'package:flutter/material.dart'; - -/// Represents a single action inside an action menu. -/// -/// [itemWrapper] can be used to wrap the [ActionMenuItemWidget] with another -/// widget (e.g. a popover). -class ActionMenuItem { - final Widget Function({double? size, Color? color}) iconBuilder; - final Function()? onPressed; - final bool Function()? selected; - final Widget Function(Widget item)? itemWrapper; - - ActionMenuItem({ - required this.iconBuilder, - required this.onPressed, - this.selected, - this.itemWrapper, - }); - - factory ActionMenuItem.icon({ - required IconData iconData, - required Function()? onPressed, - bool Function()? selected, - Widget Function(Widget item)? itemWrapper, - }) { - return ActionMenuItem( - iconBuilder: ({size, color}) { - return Icon( - iconData, - size: size, - color: color, - ); - }, - onPressed: onPressed, - selected: selected, - itemWrapper: itemWrapper, - ); - } - - factory ActionMenuItem.svg({ - required String name, - required Function()? onPressed, - bool Function()? selected, - Widget Function(Widget item)? itemWrapper, - }) { - return ActionMenuItem( - iconBuilder: ({size, color}) { - return FlowySvg( - name: name, - color: color, - width: size, - height: size, - ); - }, - onPressed: onPressed, - selected: selected, - itemWrapper: itemWrapper, - ); - } - - factory ActionMenuItem.separator() { - return ActionMenuItem( - iconBuilder: ({size, color}) { - return FlowySvg( - name: 'image_toolbar/divider', - color: color, - height: size, - ); - }, - onPressed: null, - ); - } -} - -class ActionMenuItemWidget extends StatelessWidget { - final ActionMenuItem item; - final double iconSize; - - const ActionMenuItemWidget({ - super.key, - required this.item, - this.iconSize = 20, - }); - - @override - Widget build(BuildContext context) { - final editorStyle = EditorStyle.of(context); - final isSelected = item.selected?.call() ?? false; - final color = isSelected - ? editorStyle?.selectionMenuItemSelectedIconColor - : editorStyle?.selectionMenuItemIconColor; - - var icon = item.iconBuilder(size: iconSize, color: color); - var itemWidget = Padding( - padding: const EdgeInsets.all(3), - child: item.onPressed != null - ? MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: item.onPressed, - child: icon, - ), - ) - : icon, - ); - - return item.itemWrapper?.call(itemWidget) ?? itemWidget; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/color_menu/color_picker.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/color_menu/color_picker.dart deleted file mode 100644 index 867a7e8509..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/color_menu/color_picker.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'package:appflowy_editor/src/infra/flowy_svg.dart'; -import 'package:flutter/material.dart'; - -class ColorOption { - const ColorOption({ - required this.colorHex, - required this.name, - }); - - final String colorHex; - final String name; -} - -enum _ColorType { - font, - background, -} - -class ColorPicker extends StatefulWidget { - const ColorPicker({ - super.key, - this.selectedFontColorHex, - this.selectedBackgroundColorHex, - required this.pickerBackgroundColor, - required this.fontColorOptions, - required this.backgroundColorOptions, - required this.pickerItemHoverColor, - required this.pickerItemTextColor, - required this.onSubmittedbackgroundColorHex, - required this.onSubmittedFontColorHex, - }); - - final String? selectedFontColorHex; - final String? selectedBackgroundColorHex; - final Color pickerBackgroundColor; - final Color pickerItemHoverColor; - final Color pickerItemTextColor; - final void Function(String color) onSubmittedbackgroundColorHex; - final void Function(String color) onSubmittedFontColorHex; - - final List fontColorOptions; - final List backgroundColorOptions; - - @override - State createState() => _ColorPickerState(); -} - -class _ColorPickerState extends State { - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - color: widget.pickerBackgroundColor, - boxShadow: [ - BoxShadow( - blurRadius: 5, - spreadRadius: 1, - color: Colors.black.withOpacity(0.1), - ), - ], - borderRadius: BorderRadius.circular(6.0), - ), - height: 250, - width: 220, - padding: const EdgeInsets.fromLTRB(10, 6, 10, 6), - child: ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - // font color - _buildHeader('font color'), - // padding - const SizedBox(height: 6), - _buildColorItems( - _ColorType.font, - widget.fontColorOptions, - widget.selectedFontColorHex, - ), - // background color - const SizedBox(height: 6), - _buildHeader('background color'), - const SizedBox(height: 6), - _buildColorItems( - _ColorType.background, - widget.backgroundColorOptions, - widget.selectedBackgroundColorHex, - ), - ], - ), - ), - ), - ); - } - - Widget _buildHeader(String text) { - return Text( - text, - style: const TextStyle( - color: Colors.grey, - fontWeight: FontWeight.bold, - ), - ); - } - - Widget _buildColorItems( - _ColorType type, List options, String? selectedColor) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: options - .map((e) => _buildColorItem(type, e, e.colorHex == selectedColor)) - .toList(), - ); - } - - Widget _buildColorItem(_ColorType type, ColorOption option, bool isChecked) { - return SizedBox( - height: 36, - child: InkWell( - customBorder: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(6), - ), - hoverColor: widget.pickerItemHoverColor, - onTap: () { - if (type == _ColorType.font) { - widget.onSubmittedFontColorHex(option.colorHex); - } else if (type == _ColorType.background) { - widget.onSubmittedbackgroundColorHex(option.colorHex); - } - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - // padding - const SizedBox(width: 6), - // icon - SizedBox.square( - dimension: 12, - child: Container( - decoration: BoxDecoration( - color: Color(int.tryParse(option.colorHex) ?? 0xFFFFFFFF), - shape: BoxShape.circle, - ), - ), - ), - // padding - const SizedBox(width: 10), - // text - Expanded( - child: Text( - option.name, - style: - TextStyle(fontSize: 12, color: widget.pickerItemTextColor), - ), - ), - // checkbox - if (isChecked) const FlowySvg(name: 'checkmark'), - const SizedBox(width: 6), - ], - ), - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/editor/editor_entry.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/editor/editor_entry.dart deleted file mode 100644 index f04539d94a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/editor/editor_entry.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; - -class EditorEntryWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return EditorNodeWidget( - key: context.node.key, - node: context.node, - editorState: context.editorState, - ); - } - - @override - NodeValidator get nodeValidator => ((node) { - return node.type == 'editor'; - }); -} - -class EditorNodeWidget extends StatelessWidget { - const EditorNodeWidget({ - Key? key, - required this.node, - required this.editorState, - }) : super(key: key); - - final Node node; - final EditorState editorState; - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: node.children - .map( - (child) => - editorState.service.renderPluginService.buildPluginWidget( - child is TextNode - ? NodeWidgetContext( - context: context, - node: child, - editorState: editorState, - ) - : NodeWidgetContext( - context: context, - node: child, - editorState: editorState, - ), - ), - ) - .toList(), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart deleted file mode 100644 index 4f5e760ea2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/infra/clipboard.dart'; -import 'package:appflowy_editor/src/render/action_menu/action_menu.dart'; -import 'package:appflowy_editor/src/render/action_menu/action_menu_item.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:flutter/material.dart'; - -import 'image_node_widget.dart'; - -class ImageNodeBuilder extends NodeWidgetBuilder - with ActionProvider { - @override - Widget build(NodeWidgetContext context) { - final src = context.node.attributes['image_src']; - final align = context.node.attributes['align']; - double? width; - if (context.node.attributes.containsKey('width')) { - width = context.node.attributes['width'].toDouble(); - } - return ImageNodeWidget( - key: context.node.key, - node: context.node, - src: src, - width: width, - alignment: _textToAlignment(align), - onResize: (width) { - final transaction = context.editorState.transaction - ..updateNode(context.node, { - 'width': width, - }); - context.editorState.apply(transaction); - }, - ); - } - - @override - NodeValidator get nodeValidator => ((node) { - return node.type == 'image' && - node.attributes.containsKey('image_src') && - node.attributes.containsKey('align'); - }); - - @override - List actions(NodeWidgetContext context) { - return [ - ActionMenuItem.svg( - name: 'image_toolbar/align_left', - selected: () { - final align = context.node.attributes['align']; - return _textToAlignment(align) == Alignment.centerLeft; - }, - onPressed: () => _onAlign(context, Alignment.centerLeft), - ), - ActionMenuItem.svg( - name: 'image_toolbar/align_center', - selected: () { - final align = context.node.attributes['align']; - return _textToAlignment(align) == Alignment.center; - }, - onPressed: () => _onAlign(context, Alignment.center), - ), - ActionMenuItem.svg( - name: 'image_toolbar/align_right', - selected: () { - final align = context.node.attributes['align']; - return _textToAlignment(align) == Alignment.centerRight; - }, - onPressed: () => _onAlign(context, Alignment.centerRight), - ), - ActionMenuItem.separator(), - ActionMenuItem.svg( - name: 'image_toolbar/copy', - onPressed: () { - final src = context.node.attributes['image_src']; - AppFlowyClipboard.setData(text: src); - }, - ), - ActionMenuItem.svg( - name: 'image_toolbar/delete', - onPressed: () { - final transaction = context.editorState.transaction - ..deleteNode(context.node); - context.editorState.apply(transaction); - }, - ), - ]; - } - - Alignment _textToAlignment(String text) { - if (text == 'left') { - return Alignment.centerLeft; - } else if (text == 'right') { - return Alignment.centerRight; - } - return Alignment.center; - } - - String _alignmentToText(Alignment alignment) { - if (alignment == Alignment.centerLeft) { - return 'left'; - } else if (alignment == Alignment.centerRight) { - return 'right'; - } - return 'center'; - } - - void _onAlign(NodeWidgetContext context, Alignment alignment) { - final transaction = context.editorState.transaction - ..updateNode(context.node, { - 'align': _alignmentToText(alignment), - }); - context.editorState.apply(transaction); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart deleted file mode 100644 index 27812d996e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart +++ /dev/null @@ -1,271 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/extensions/object_extensions.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:flutter/material.dart'; - -class ImageNodeWidget extends StatefulWidget { - const ImageNodeWidget({ - Key? key, - required this.node, - required this.src, - this.width, - required this.alignment, - required this.onResize, - }) : super(key: key); - - final Node node; - final String src; - final double? width; - final Alignment alignment; - final void Function(double width) onResize; - - @override - State createState() => _ImageNodeWidgetState(); -} - -class _ImageNodeWidgetState extends State - with SelectableMixin { - RenderBox get _renderBox => context.findRenderObject() as RenderBox; - - final _imageKey = GlobalKey(); - - double? _imageWidth; - double _initial = 0; - double _distance = 0; - bool _onFocus = false; - - ImageStream? _imageStream; - late ImageStreamListener _imageStreamListener; - - @override - void initState() { - super.initState(); - - _imageWidth = widget.width; - _imageStreamListener = ImageStreamListener( - (image, _) { - _imageWidth = _imageKey.currentContext - ?.findRenderObject() - ?.unwrapOrNull() - ?.size - .width; - }, - ); - } - - @override - void dispose() { - _imageStream?.removeListener(_imageStreamListener); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - // only support network image. - return Container( - key: _imageKey, - padding: const EdgeInsets.only(top: 8, bottom: 8), - child: _buildNetworkImage(context), - ); - } - - @override - bool get shouldCursorBlink => false; - - @override - CursorStyle get cursorStyle => CursorStyle.borderLine; - - @override - Position start() { - return Position(path: widget.node.path, offset: 0); - } - - @override - Position end() { - return start(); - } - - @override - Position getPositionInOffset(Offset start) { - return end(); - } - - @override - Rect? getCursorRectInPosition(Position position) { - final size = _renderBox.size; - return Rect.fromLTWH(-size.width / 2.0, 0, size.width, size.height); - } - - @override - List getRectsInSelection(Selection selection) { - final renderBox = context.findRenderObject() as RenderBox; - return [Offset.zero & renderBox.size]; - } - - @override - Selection getSelectionInRange(Offset start, Offset end) { - if (start <= end) { - return Selection(start: this.start(), end: this.end()); - } else { - return Selection(start: this.end(), end: this.start()); - } - } - - @override - Offset localToGlobal(Offset offset) { - final renderBox = context.findRenderObject() as RenderBox; - return renderBox.localToGlobal(offset); - } - - Widget _buildNetworkImage(BuildContext context) { - return Align( - alignment: widget.alignment, - child: MouseRegion( - onEnter: (event) => setState(() { - _onFocus = true; - }), - onExit: (event) => setState(() { - _onFocus = false; - }), - child: _buildResizableImage(context), - ), - ); - } - - Widget _buildResizableImage(BuildContext context) { - final networkImage = Image.network( - widget.src, - width: _imageWidth == null ? null : _imageWidth! - _distance, - gaplessPlayback: true, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null || - loadingProgress.cumulativeBytesLoaded == - loadingProgress.expectedTotalBytes) return child; - return _buildLoading(context); - }, - errorBuilder: (context, error, stackTrace) { - // _imageWidth ??= defaultMaxTextNodeWidth; - return _buildError(context); - }, - ); - if (_imageWidth == null) { - _imageStream = networkImage.image.resolve(const ImageConfiguration()) - ..addListener(_imageStreamListener); - } - return Stack( - children: [ - networkImage, - _buildEdgeGesture( - context, - top: 0, - left: 0, - bottom: 0, - width: 5, - onUpdate: (distance) { - setState(() { - _distance = distance; - }); - }, - ), - _buildEdgeGesture( - context, - top: 0, - right: 0, - bottom: 0, - width: 5, - onUpdate: (distance) { - setState(() { - _distance = -distance; - }); - }, - ), - ], - ); - } - - Widget _buildLoading(BuildContext context) { - return SizedBox( - height: 150, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox.fromSize( - size: const Size(18, 18), - child: const CircularProgressIndicator(), - ), - SizedBox.fromSize( - size: const Size(10, 10), - ), - const Text('Loading'), - ], - ), - ); - } - - Widget _buildError(BuildContext context) { - return Container( - height: 100, - width: _imageWidth, - alignment: Alignment.center, - padding: const EdgeInsets.only(top: 8.0, bottom: 8.0), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(4.0)), - border: Border.all(width: 1, color: Colors.black), - ), - child: const Text('Could not load the image'), - ); - } - - Widget _buildEdgeGesture( - BuildContext context, { - double? top, - double? left, - double? right, - double? bottom, - double? width, - void Function(double distance)? onUpdate, - }) { - return Positioned( - top: top, - left: left, - right: right, - bottom: bottom, - width: width, - child: GestureDetector( - onHorizontalDragStart: (details) { - _initial = details.globalPosition.dx; - }, - onHorizontalDragUpdate: (details) { - if (onUpdate != null) { - onUpdate(details.globalPosition.dx - _initial); - } - }, - onHorizontalDragEnd: (details) { - _imageWidth = _imageWidth! - _distance; - _initial = 0; - _distance = 0; - - widget.onResize(_imageWidth!); - }, - child: MouseRegion( - cursor: SystemMouseCursors.resizeLeftRight, - child: _onFocus - ? Center( - child: Container( - height: 40, - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.2), - borderRadius: const BorderRadius.all( - Radius.circular(5.0), - ), - ), - ), - ) - : null, - ), - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_upload_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_upload_widget.dart deleted file mode 100644 index a8909d16c5..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/image/image_upload_widget.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/infra/flowy_svg.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; -import 'package:appflowy_editor/src/render/style/editor_style.dart'; -import 'package:flutter/material.dart'; - -OverlayEntry? _imageUploadMenu; -EditorState? _editorState; -void showImageUploadMenu( - EditorState editorState, - SelectionMenuService menuService, - BuildContext context, -) { - menuService.dismiss(); - - _imageUploadMenu?.remove(); - _imageUploadMenu = OverlayEntry(builder: (context) { - return Positioned( - top: menuService.topLeft.dy, - left: menuService.topLeft.dx, - child: Material( - child: ImageUploadMenu( - editorState: editorState, - onSubmitted: (text) { - // _dismissImageUploadMenu(); - editorState.insertImageNode(text); - }, - onUpload: (text) { - // _dismissImageUploadMenu(); - editorState.insertImageNode(text); - }, - ), - ), - ); - }); - - Overlay.of(context)?.insert(_imageUploadMenu!); - - editorState.service.selectionService.currentSelection - .addListener(_dismissImageUploadMenu); -} - -void _dismissImageUploadMenu() { - _imageUploadMenu?.remove(); - _imageUploadMenu = null; - - _editorState?.service.selectionService.currentSelection - .removeListener(_dismissImageUploadMenu); - _editorState = null; -} - -class ImageUploadMenu extends StatefulWidget { - const ImageUploadMenu({ - Key? key, - required this.onSubmitted, - required this.onUpload, - this.editorState, - }) : super(key: key); - - final void Function(String text) onSubmitted; - final void Function(String text) onUpload; - final EditorState? editorState; - - @override - State createState() => _ImageUploadMenuState(); -} - -class _ImageUploadMenuState extends State { - final _textEditingController = TextEditingController(); - final _focusNode = FocusNode(); - - EditorStyle? get style => widget.editorState?.editorStyle; - - @override - void initState() { - super.initState(); - _focusNode.requestFocus(); - } - - @override - void dispose() { - _focusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Container( - width: 300, - padding: const EdgeInsets.all(24.0), - decoration: BoxDecoration( - color: style?.selectionMenuBackgroundColor ?? Colors.white, - boxShadow: [ - BoxShadow( - blurRadius: 5, - spreadRadius: 1, - color: Colors.black.withOpacity(0.1), - ), - ], - // borderRadius: BorderRadius.circular(6.0), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildHeader(context), - const SizedBox(height: 16.0), - _buildInput(), - const SizedBox(height: 18.0), - _buildUploadButton(context), - ], - ), - ); - } - - Widget _buildHeader(BuildContext context) { - return Text( - 'URL Image', - textAlign: TextAlign.left, - style: TextStyle( - fontSize: 14.0, - color: style?.selectionMenuItemTextColor ?? Colors.black, - fontWeight: FontWeight.w500, - ), - ); - } - - Widget _buildInput() { - return TextField( - focusNode: _focusNode, - style: const TextStyle(fontSize: 14.0), - textAlign: TextAlign.left, - controller: _textEditingController, - onSubmitted: widget.onSubmitted, - decoration: InputDecoration( - hintText: 'URL', - hintStyle: const TextStyle(fontSize: 14.0), - contentPadding: const EdgeInsets.all(16.0), - isDense: true, - suffixIcon: IconButton( - padding: const EdgeInsets.all(4.0), - icon: const FlowySvg( - name: 'clear', - width: 24, - height: 24, - ), - onPressed: () { - _textEditingController.clear(); - }, - ), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(12.0)), - borderSide: BorderSide(color: Color(0xFFBDBDBD)), - ), - ), - ); - } - - Widget _buildUploadButton(BuildContext context) { - return SizedBox( - width: 170, - height: 48, - child: TextButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(const Color(0xFF00BCF0)), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12.0), - ), - ), - ), - onPressed: () { - widget.onUpload(_textEditingController.text); - }, - child: const Text( - 'Upload', - style: TextStyle(color: Colors.white, fontSize: 14.0), - ), - ), - ); - } -} - -extension on EditorState { - void insertImageNode(String src) { - final selection = service.selectionService.currentSelection.value; - if (selection == null) { - return; - } - final imageNode = Node( - type: 'image', - attributes: { - 'image_src': src, - 'align': 'center', - }, - ); - final transaction = this.transaction; - transaction.insertNode( - selection.start.path, - imageNode, - ); - apply(transaction); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/link_menu/link_menu.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/link_menu/link_menu.dart deleted file mode 100644 index 58e8111793..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/link_menu/link_menu.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/infra/flowy_svg.dart'; -import 'package:appflowy_editor/src/render/style/editor_style.dart'; -import 'package:flutter/material.dart'; - -class LinkMenu extends StatefulWidget { - const LinkMenu({ - Key? key, - this.linkText, - this.editorState, - required this.onSubmitted, - required this.onOpenLink, - required this.onCopyLink, - required this.onRemoveLink, - required this.onFocusChange, - }) : super(key: key); - - final String? linkText; - final EditorState? editorState; - final void Function(String text) onSubmitted; - final VoidCallback onOpenLink; - final VoidCallback onCopyLink; - final VoidCallback onRemoveLink; - final void Function(bool value) onFocusChange; - - @override - State createState() => _LinkMenuState(); -} - -class _LinkMenuState extends State { - final _textEditingController = TextEditingController(); - final _focusNode = FocusNode(); - - EditorStyle? get style => widget.editorState?.editorStyle; - - @override - void initState() { - super.initState(); - _textEditingController.text = widget.linkText ?? ''; - _focusNode.requestFocus(); - _focusNode.addListener(_onFocusChange); - } - - @override - void dispose() { - _textEditingController.dispose(); - _focusNode.removeListener(_onFocusChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 350, - child: Container( - decoration: BoxDecoration( - color: style?.selectionMenuBackgroundColor ?? Colors.white, - boxShadow: [ - BoxShadow( - blurRadius: 5, - spreadRadius: 1, - color: Colors.black.withOpacity(0.1), - ), - ], - borderRadius: BorderRadius.circular(6.0), - ), - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - _buildHeader(), - const SizedBox(height: 16.0), - _buildInput(), - const SizedBox(height: 16.0), - if (widget.linkText != null) ...[ - _buildIconButton( - iconName: 'link', - color: style?.selectionMenuItemIconColor, - text: 'Open link', - onPressed: widget.onOpenLink, - ), - _buildIconButton( - iconName: 'copy', - color: style?.selectionMenuItemIconColor, - text: 'Copy link', - onPressed: widget.onCopyLink, - ), - _buildIconButton( - iconName: 'delete', - color: style?.selectionMenuItemIconColor, - text: 'Remove link', - onPressed: widget.onRemoveLink, - ), - ] - ], - ), - ), - ), - ); - } - - Widget _buildHeader() { - return const Text( - 'Add your link', - style: TextStyle( - color: Colors.grey, - fontWeight: FontWeight.bold, - ), - ); - } - - Widget _buildInput() { - return TextField( - focusNode: _focusNode, - style: const TextStyle(fontSize: 14.0), - textAlign: TextAlign.left, - controller: _textEditingController, - onSubmitted: widget.onSubmitted, - decoration: InputDecoration( - hintText: 'URL', - hintStyle: const TextStyle(fontSize: 14.0), - contentPadding: const EdgeInsets.all(16.0), - isDense: true, - suffixIcon: IconButton( - padding: const EdgeInsets.all(4.0), - icon: const FlowySvg( - name: 'clear', - width: 24, - height: 24, - ), - onPressed: () { - _textEditingController.clear(); - }, - ), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(12.0)), - borderSide: BorderSide(color: Color(0xFFBDBDBD)), - ), - ), - ); - } - - Widget _buildIconButton({ - required String iconName, - Color? color, - required String text, - required VoidCallback onPressed, - }) { - return TextButton.icon( - icon: FlowySvg( - name: iconName, - color: color, - ), - style: TextButton.styleFrom( - minimumSize: const Size.fromHeight(40), - padding: EdgeInsets.zero, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - alignment: Alignment.centerLeft, - ), - label: Text( - text, - textAlign: TextAlign.left, - style: TextStyle( - color: style?.selectionMenuItemTextColor ?? Colors.black, - fontSize: 14.0, - ), - ), - onPressed: onPressed, - ); - } - - void _onFocusChange() { - widget.onFocusChange(_focusNode.hasFocus); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart deleted file mode 100644 index 7fb2cee2cb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; - -abstract class BuiltInTextWidget extends StatefulWidget { - const BuiltInTextWidget({ - Key? key, - }) : super(key: key); - - EditorState get editorState; - TextNode get textNode; -} - -mixin BuiltInTextWidgetMixin on State - implements DefaultSelectable { - @override - Widget build(BuildContext context) { - if (widget.textNode.children.isEmpty) { - return buildWithSingle(context); - } else { - return buildWithChildren(context); - } - } - - Widget buildWithSingle(BuildContext context); - - Widget buildWithChildren(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - buildWithSingle(context), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // TODO: customize - const SizedBox( - width: 20, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: widget.textNode.children - .map( - (child) => widget.editorState.service.renderPluginService - .buildPluginWidget( - child is TextNode - ? NodeWidgetContext( - context: context, - node: child, - editorState: widget.editorState, - ) - : NodeWidgetContext( - context: context, - node: child, - editorState: widget.editorState, - ), - ), - ) - .toList(), - ), - ) - ], - ) - ], - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart deleted file mode 100644 index 3f6927df4c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; -import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:appflowy_editor/src/render/style/plugin_styles.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; -import 'package:appflowy_editor/src/extensions/theme_extension.dart'; - -class BulletedListTextNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return BulletedListTextNodeWidget( - key: context.node.key, - textNode: context.node, - editorState: context.editorState, - ); - } - - @override - NodeValidator get nodeValidator => ((node) { - return true; - }); -} - -class BulletedListTextNodeWidget extends BuiltInTextWidget { - const BulletedListTextNodeWidget({ - Key? key, - required this.textNode, - required this.editorState, - }) : super(key: key); - - @override - final TextNode textNode; - @override - final EditorState editorState; - - @override - State createState() => - _BulletedListTextNodeWidgetState(); -} - -// customize - -class _BulletedListTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable, BuiltInTextWidgetMixin { - @override - final iconKey = GlobalKey(); - - final _richTextKey = GlobalKey(debugLabel: 'bulleted_list_text'); - - @override - SelectableMixin get forward => - _richTextKey.currentState as SelectableMixin; - - @override - Offset get baseOffset { - return super.baseOffset.translate(0, padding.top); - } - - BulletedListPluginStyle get style => - Theme.of(context).extensionOrNull() ?? - BulletedListPluginStyle.light; - - EdgeInsets get padding => style.padding( - widget.editorState, - widget.textNode, - ); - - TextStyle get textStyle => style.textStyle( - widget.editorState, - widget.textNode, - ); - - Widget get icon => style.icon( - widget.editorState, - widget.textNode, - ); - - @override - Widget buildWithSingle(BuildContext context) { - return Padding( - padding: padding, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - key: iconKey, - child: icon, - ), - Flexible( - child: FlowyRichText( - key: _richTextKey, - placeholderText: 'List', - textSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - placeholderTextSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - lineHeight: widget.editorState.editorStyle.lineHeight, - textNode: widget.textNode, - editorState: widget.editorState, - ), - ) - ], - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart deleted file mode 100644 index d2b9cbbda2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; - -import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/theme_extension.dart'; - -class CheckboxNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return CheckboxNodeWidget( - key: context.node.key, - textNode: context.node, - editorState: context.editorState, - ); - } - - @override - NodeValidator get nodeValidator => ((node) { - return node.attributes.containsKey(BuiltInAttributeKey.checkbox); - }); -} - -class CheckboxNodeWidget extends BuiltInTextWidget { - const CheckboxNodeWidget({ - Key? key, - required this.textNode, - required this.editorState, - }) : super(key: key); - - @override - final TextNode textNode; - @override - final EditorState editorState; - - @override - State createState() => _CheckboxNodeWidgetState(); -} - -class _CheckboxNodeWidgetState extends State - with SelectableMixin, DefaultSelectable, BuiltInTextWidgetMixin { - @override - final iconKey = GlobalKey(); - - final _richTextKey = GlobalKey(debugLabel: 'checkbox_text'); - - @override - SelectableMixin get forward => - _richTextKey.currentState as SelectableMixin; - - @override - Offset get baseOffset { - return super.baseOffset.translate(0, padding.top); - } - - CheckboxPluginStyle get style => - Theme.of(context).extensionOrNull() ?? - CheckboxPluginStyle.light; - - EdgeInsets get padding => style.padding( - widget.editorState, - widget.textNode, - ); - - TextStyle get textStyle => style.textStyle( - widget.editorState, - widget.textNode, - ); - - Widget get icon => style.icon( - widget.editorState, - widget.textNode, - ); - - @override - Widget buildWithSingle(BuildContext context) { - final check = widget.textNode.attributes.check; - return Padding( - padding: padding, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - key: iconKey, - behavior: HitTestBehavior.opaque, - onTap: () async { - await widget.editorState.formatTextToCheckbox( - widget.editorState, - !check, - textNode: widget.textNode, - ); - }, - child: icon, - ), - Flexible( - child: FlowyRichText( - key: _richTextKey, - placeholderText: 'To-do', - lineHeight: widget.editorState.editorStyle.lineHeight, - textNode: widget.textNode, - textSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - placeholderTextSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - editorState: widget.editorState, - ), - ), - ], - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/default_selectable.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/default_selectable.dart deleted file mode 100644 index 9a1ea224ef..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/default_selectable.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:flutter/material.dart'; - -mixin DefaultSelectable { - SelectableMixin get forward; - - GlobalKey? get iconKey; - - Offset get baseOffset { - if (iconKey != null) { - final renderBox = iconKey!.currentContext?.findRenderObject(); - if (renderBox is RenderBox) { - return Offset(renderBox.size.width, 0); - } - } - return Offset.zero; - } - - Position getPositionInOffset(Offset start) => - forward.getPositionInOffset(start); - - Rect? getCursorRectInPosition(Position position) => - forward.getCursorRectInPosition(position)?.shift(baseOffset); - - List getRectsInSelection(Selection selection) => forward - .getRectsInSelection(selection) - .map((rect) => rect.shift(baseOffset)) - .toList(growable: false); - - Selection getSelectionInRange(Offset start, Offset end) => - forward.getSelectionInRange(start, end); - - Offset localToGlobal(Offset offset) => - forward.localToGlobal(offset) - baseOffset; - - Selection? getWordBoundaryInOffset(Offset offset) => - forward.getWordBoundaryInOffset(offset); - - Selection? getWordBoundaryInPosition(Position position) => - forward.getWordBoundaryInPosition(position); - - Position start() => forward.start(); - - Position end() => forward.end(); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart deleted file mode 100644 index 2c92b43a31..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart +++ /dev/null @@ -1,336 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; -import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; -import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; - -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart'; - -const _kRichTextDebugMode = false; - -typedef FlowyTextSpanDecorator = TextSpan Function(TextSpan textSpan); - -class FlowyRichText extends StatefulWidget { - const FlowyRichText({ - Key? key, - this.cursorHeight, - this.cursorWidth = 1.5, - this.lineHeight = 1.0, - this.textSpanDecorator, - this.placeholderText = ' ', - this.placeholderTextSpanDecorator, - required this.textNode, - required this.editorState, - }) : super(key: key); - - final TextNode textNode; - final EditorState editorState; - final double? cursorHeight; - final double cursorWidth; - final double lineHeight; - final FlowyTextSpanDecorator? textSpanDecorator; - final String placeholderText; - final FlowyTextSpanDecorator? placeholderTextSpanDecorator; - - @override - State createState() => _FlowyRichTextState(); -} - -class _FlowyRichTextState extends State with SelectableMixin { - var _textKey = GlobalKey(); - final _placeholderTextKey = GlobalKey(); - - RenderParagraph get _renderParagraph => - _textKey.currentContext?.findRenderObject() as RenderParagraph; - - RenderParagraph? get _placeholderRenderParagraph => - _placeholderTextKey.currentContext?.findRenderObject() as RenderParagraph; - - @override - void didUpdateWidget(covariant FlowyRichText oldWidget) { - super.didUpdateWidget(oldWidget); - - // https://github.com/flutter/flutter/issues/110342 - if (_textKey.currentWidget is RichText) { - // Force refresh the RichText widget. - _textKey = GlobalKey(); - } - } - - @override - Widget build(BuildContext context) { - return _buildRichText(context); - } - - @override - Position start() => Position(path: widget.textNode.path, offset: 0); - - @override - Position end() => Position( - path: widget.textNode.path, offset: widget.textNode.toPlainText().length); - - @override - Rect? getCursorRectInPosition(Position position) { - final textPosition = TextPosition(offset: position.offset); - - var cursorHeight = _renderParagraph.getFullHeightForCaret(textPosition); - var cursorOffset = - _renderParagraph.getOffsetForCaret(textPosition, Rect.zero); - if (cursorHeight == null) { - cursorHeight = - _placeholderRenderParagraph?.getFullHeightForCaret(textPosition); - cursorOffset = _placeholderRenderParagraph?.getOffsetForCaret( - textPosition, Rect.zero) ?? - Offset.zero; - } - if (widget.cursorHeight != null && cursorHeight != null) { - cursorOffset = Offset( - cursorOffset.dx, - cursorOffset.dy + (cursorHeight - widget.cursorHeight!) / 2, - ); - cursorHeight = widget.cursorHeight; - } - final rect = Rect.fromLTWH( - cursorOffset.dx - (widget.cursorWidth / 2.0), - cursorOffset.dy, - widget.cursorWidth, - cursorHeight ?? 16.0, - ); - return rect; - } - - @override - Position getPositionInOffset(Offset start) { - final offset = _renderParagraph.globalToLocal(start); - final baseOffset = _renderParagraph.getPositionForOffset(offset).offset; - return Position(path: widget.textNode.path, offset: baseOffset); - } - - @override - Selection? getWordBoundaryInOffset(Offset offset) { - final localOffset = _renderParagraph.globalToLocal(offset); - final textPosition = _renderParagraph.getPositionForOffset(localOffset); - final textRange = _renderParagraph.getWordBoundary(textPosition); - final start = Position(path: widget.textNode.path, offset: textRange.start); - final end = Position(path: widget.textNode.path, offset: textRange.end); - return Selection(start: start, end: end); - } - - @override - Selection? getWordBoundaryInPosition(Position position) { - final textPosition = TextPosition(offset: position.offset); - final textRange = _renderParagraph.getWordBoundary(textPosition); - final start = Position(path: widget.textNode.path, offset: textRange.start); - final end = Position(path: widget.textNode.path, offset: textRange.end); - return Selection(start: start, end: end); - } - - @override - List getRectsInSelection(Selection selection) { - assert(selection.isSingle && - selection.start.path.equals(widget.textNode.path)); - - final textSelection = TextSelection( - baseOffset: selection.start.offset, - extentOffset: selection.end.offset, - ); - final rects = _renderParagraph - .getBoxesForSelection(textSelection, boxHeightStyle: BoxHeightStyle.max) - .map((box) => box.toRect()) - .toList(growable: false); - if (rects.isEmpty) { - // If the rich text widget does not contain any text, - // there will be no selection boxes, - // so we need to return to the default selection. - return [Rect.fromLTWH(0, 0, 0, _renderParagraph.size.height)]; - } - return rects; - } - - @override - Selection getSelectionInRange(Offset start, Offset end) { - final localStart = _renderParagraph.globalToLocal(start); - final localEnd = _renderParagraph.globalToLocal(end); - final baseOffset = _renderParagraph.getPositionForOffset(localStart).offset; - final extentOffset = _renderParagraph.getPositionForOffset(localEnd).offset; - return Selection.single( - path: widget.textNode.path, - startOffset: baseOffset, - endOffset: extentOffset, - ); - } - - @override - Offset localToGlobal(Offset offset) { - return _renderParagraph.localToGlobal(offset); - } - - Widget _buildRichText(BuildContext context) { - return MouseRegion( - cursor: SystemMouseCursors.text, - child: widget.textNode.toPlainText().isEmpty - ? Stack( - children: [ - _buildPlaceholderText(context), - _buildSingleRichText(context), - ], - ) - : _buildSingleRichText(context), - ); - } - - Widget _buildPlaceholderText(BuildContext context) { - final textSpan = _placeholderTextSpan; - return RichText( - key: _placeholderTextKey, - textHeightBehavior: const TextHeightBehavior( - applyHeightToFirstAscent: false, applyHeightToLastDescent: false), - text: widget.placeholderTextSpanDecorator != null - ? widget.placeholderTextSpanDecorator!(textSpan) - : textSpan, - ); - } - - Widget _buildSingleRichText(BuildContext context) { - final textSpan = _textSpan; - return RichText( - key: _textKey, - textHeightBehavior: const TextHeightBehavior( - applyHeightToFirstAscent: false, - applyHeightToLastDescent: false, - ), - text: widget.textSpanDecorator != null - ? widget.textSpanDecorator!(textSpan) - : textSpan, - ); - } - - TextSpan get _placeholderTextSpan { - final placeholderTextStyle = - widget.editorState.editorStyle.placeholderTextStyle; - return TextSpan( - children: [ - TextSpan( - text: widget.placeholderText, - style: placeholderTextStyle, - ), - ], - ); - } - - TextSpan get _textSpan { - var offset = 0; - List textSpans = []; - final style = widget.editorState.editorStyle; - final textInserts = widget.textNode.delta.whereType(); - for (final textInsert in textInserts) { - var textStyle = style.textStyle!; - GestureRecognizer? recognizer; - final attributes = textInsert.attributes; - if (attributes != null) { - if (attributes.bold == true) { - textStyle = textStyle.combine(style.bold); - } - if (attributes.italic == true) { - textStyle = textStyle.combine(style.italic); - } - if (attributes.underline == true) { - textStyle = textStyle.combine(style.underline); - } - if (attributes.strikethrough == true) { - textStyle = textStyle.combine(style.strikethrough); - } - if (attributes.href != null) { - textStyle = textStyle.combine(style.href); - recognizer = _buildTapHrefGestureRecognizer( - attributes.href!, - Selection.single( - path: widget.textNode.path, - startOffset: offset, - endOffset: offset + textInsert.length, - ), - ); - } - if (attributes.code == true) { - textStyle = textStyle.combine(style.code); - } - if (attributes.backgroundColor != null) { - textStyle = textStyle.combine( - TextStyle(backgroundColor: attributes.backgroundColor), - ); - } - if (attributes.color != null) { - textStyle = textStyle.combine( - TextStyle(color: attributes.color), - ); - } - } - offset += textInsert.length; - textSpans.add( - TextSpan( - text: textInsert.text, - style: textStyle, - recognizer: recognizer, - ), - ); - } - if (_kRichTextDebugMode) { - textSpans.add( - TextSpan( - text: '${widget.textNode.path}', - style: const TextStyle( - backgroundColor: Colors.red, - fontSize: 16.0, - ), - ), - ); - } - return TextSpan( - children: textSpans, - ); - } - - GestureRecognizer _buildTapHrefGestureRecognizer( - String href, Selection selection) { - Timer? timer; - var tapCount = 0; - final tapGestureRecognizer = TapGestureRecognizer() - ..onTap = () async { - // implement a simple double tap logic - tapCount += 1; - timer?.cancel(); - - if (tapCount == 2) { - tapCount = 0; - safeLaunchUrl(href); - return; - } - - timer = Timer(const Duration(milliseconds: 200), () { - tapCount = 0; - widget.editorState.service.selectionService - .updateSelection(selection); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - showLinkMenu( - context, - widget.editorState, - customSelection: selection, - ); - }); - }); - }; - return tapGestureRecognizer; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart deleted file mode 100644 index f8f5bd0f92..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; -import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:appflowy_editor/src/render/style/plugin_styles.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; -import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; -import 'package:appflowy_editor/src/extensions/theme_extension.dart'; - -class HeadingTextNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return HeadingTextNodeWidget( - key: context.node.key, - textNode: context.node, - editorState: context.editorState, - ); - } - - @override - NodeValidator get nodeValidator => ((node) { - return node.attributes.heading != null; - }); -} - -class HeadingTextNodeWidget extends BuiltInTextWidget { - const HeadingTextNodeWidget({ - Key? key, - required this.textNode, - required this.editorState, - }) : super(key: key); - - @override - final TextNode textNode; - @override - final EditorState editorState; - - @override - State createState() => _HeadingTextNodeWidgetState(); -} - -// customize -class _HeadingTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { - @override - GlobalKey? get iconKey => null; - - final _richTextKey = GlobalKey(debugLabel: 'heading_text'); - - @override - SelectableMixin get forward => - _richTextKey.currentState as SelectableMixin; - - @override - Offset get baseOffset { - return padding.topLeft; - } - - HeadingPluginStyle get style => - Theme.of(context).extensionOrNull() ?? - HeadingPluginStyle.light; - - EdgeInsets get padding => style.padding( - widget.editorState, - widget.textNode, - ); - - TextStyle get textStyle => style.textStyle( - widget.editorState, - widget.textNode, - ); - - @override - Widget build(BuildContext context) { - return Padding( - padding: padding, - child: FlowyRichText( - key: _richTextKey, - placeholderText: 'Heading', - placeholderTextSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - textSpanDecorator: (textSpan) => textSpan.updateTextStyle(textStyle), - lineHeight: widget.editorState.editorStyle.lineHeight, - textNode: widget.textNode, - editorState: widget.editorState, - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart deleted file mode 100644 index 60698d6aad..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; -import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:appflowy_editor/src/render/style/plugin_styles.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; -import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; -import 'package:appflowy_editor/src/extensions/theme_extension.dart'; - -class NumberListTextNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return NumberListTextNodeWidget( - key: context.node.key, - textNode: context.node, - editorState: context.editorState, - ); - } - - @override - NodeValidator get nodeValidator => ((node) { - return node.attributes.number != null; - }); -} - -class NumberListTextNodeWidget extends BuiltInTextWidget { - const NumberListTextNodeWidget({ - Key? key, - required this.textNode, - required this.editorState, - }) : super(key: key); - - @override - final TextNode textNode; - @override - final EditorState editorState; - - @override - State createState() => - _NumberListTextNodeWidgetState(); -} - -class _NumberListTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { - @override - final iconKey = GlobalKey(); - - final _richTextKey = GlobalKey(debugLabel: 'number_list_text'); - - @override - SelectableMixin get forward => - _richTextKey.currentState as SelectableMixin; - - @override - Offset get baseOffset { - return super.baseOffset.translate(0, padding.top); - } - - NumberListPluginStyle get style => - Theme.of(context).extensionOrNull() ?? - NumberListPluginStyle.light; - - EdgeInsets get padding => style.padding( - widget.editorState, - widget.textNode, - ); - - TextStyle get textStyle => style.textStyle( - widget.editorState, - widget.textNode, - ); - - Widget get icon => style.icon( - widget.editorState, - widget.textNode, - ); - - @override - Widget build(BuildContext context) { - return Padding( - padding: padding, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - key: iconKey, - child: icon, - ), - Flexible( - child: FlowyRichText( - key: _richTextKey, - placeholderText: 'List', - textNode: widget.textNode, - editorState: widget.editorState, - lineHeight: widget.editorState.editorStyle.lineHeight, - placeholderTextSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - textSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - ), - ), - ], - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart deleted file mode 100644 index 370d328d1e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; -import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:appflowy_editor/src/render/style/plugin_styles.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; -import 'package:appflowy_editor/src/extensions/theme_extension.dart'; - -class QuotedTextNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return QuotedTextNodeWidget( - key: context.node.key, - textNode: context.node, - editorState: context.editorState, - ); - } - - @override - NodeValidator get nodeValidator => ((node) { - return true; - }); -} - -class QuotedTextNodeWidget extends BuiltInTextWidget { - const QuotedTextNodeWidget({ - Key? key, - required this.textNode, - required this.editorState, - }) : super(key: key); - - @override - final TextNode textNode; - @override - final EditorState editorState; - - @override - State createState() => _QuotedTextNodeWidgetState(); -} - -// customize - -class _QuotedTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { - @override - final iconKey = GlobalKey(); - - final _richTextKey = GlobalKey(debugLabel: 'quoted_text'); - - @override - SelectableMixin get forward => - _richTextKey.currentState as SelectableMixin; - - @override - Offset get baseOffset { - return super.baseOffset.translate(0, padding.top); - } - - QuotedTextPluginStyle get style => - Theme.of(context).extensionOrNull() ?? - QuotedTextPluginStyle.light; - - EdgeInsets get padding => style.padding( - widget.editorState, - widget.textNode, - ); - - TextStyle get textStyle => style.textStyle( - widget.editorState, - widget.textNode, - ); - - Widget get icon => style.icon( - widget.editorState, - widget.textNode, - ); - - @override - Widget build(BuildContext context) { - return Padding( - padding: padding, - child: IntrinsicHeight( - child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - key: iconKey, - child: icon, - ), - Flexible( - child: FlowyRichText( - key: _richTextKey, - placeholderText: 'Quote', - textNode: widget.textNode, - textSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - placeholderTextSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - lineHeight: widget.editorState.editorStyle.lineHeight, - editorState: widget.editorState, - ), - ), - ], - ), - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart deleted file mode 100644 index b739211951..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; -import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:appflowy_editor/src/render/style/editor_style.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; - -class RichTextNodeWidgetBuilder extends NodeWidgetBuilder { - @override - Widget build(NodeWidgetContext context) { - return RichTextNodeWidget( - key: context.node.key, - textNode: context.node, - editorState: context.editorState, - ); - } - - @override - NodeValidator get nodeValidator => ((node) { - return true; - }); -} - -class RichTextNodeWidget extends BuiltInTextWidget { - const RichTextNodeWidget({ - Key? key, - required this.textNode, - required this.editorState, - }) : super(key: key); - - @override - final TextNode textNode; - @override - final EditorState editorState; - - @override - State createState() => _RichTextNodeWidgetState(); -} - -// customize - -class _RichTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable, BuiltInTextWidgetMixin { - @override - GlobalKey? get iconKey => null; - - final _richTextKey = GlobalKey(debugLabel: 'rich_text'); - - @override - SelectableMixin get forward => - _richTextKey.currentState as SelectableMixin; - - @override - Offset get baseOffset { - return textPadding.topLeft; - } - - EditorStyle get style => widget.editorState.editorStyle; - - EdgeInsets get textPadding => style.textPadding!; - - TextStyle get textStyle => style.textStyle!; - - @override - Widget buildWithSingle(BuildContext context) { - return Padding( - padding: textPadding, - child: FlowyRichText( - key: _richTextKey, - textNode: widget.textNode, - textSpanDecorator: (textSpan) => textSpan, - placeholderTextSpanDecorator: (textSpan) => - textSpan.updateTextStyle(textStyle), - lineHeight: widget.editorState.editorStyle.lineHeight, - editorState: widget.editorState, - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/cursor_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/cursor_widget.dart deleted file mode 100644 index a7b68d410d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/cursor_widget.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'dart:async'; - -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:flutter/material.dart'; - -class CursorWidget extends StatefulWidget { - const CursorWidget({ - Key? key, - required this.layerLink, - required this.rect, - required this.color, - this.blinkingInterval = 0.5, - this.shouldBlink = true, - this.cursorStyle = CursorStyle.verticalLine, - }) : super(key: key); - - final double blinkingInterval; // milliseconds - final bool shouldBlink; - final CursorStyle cursorStyle; - final Color color; - final Rect rect; - final LayerLink layerLink; - - @override - State createState() => CursorWidgetState(); -} - -class CursorWidgetState extends State { - bool showCursor = true; - late Timer timer; - - @override - void initState() { - super.initState(); - - timer = _initTimer(); - } - - @override - void dispose() { - timer.cancel(); - super.dispose(); - } - - Timer _initTimer() { - return Timer.periodic( - Duration(milliseconds: (widget.blinkingInterval * 1000).toInt()), - (timer) { - setState(() { - showCursor = !showCursor; - }); - }); - } - - /// force the cursor widget to show for a while - show() { - setState(() { - showCursor = true; - }); - timer.cancel(); - timer = _initTimer(); - } - - @override - Widget build(BuildContext context) { - return Positioned.fromRect( - rect: widget.rect, - child: CompositedTransformFollower( - link: widget.layerLink, - offset: widget.rect.topCenter, - showWhenUnlinked: true, - // Ignore the gestures in cursor - // to solve the problem that cursor area cannot be selected. - child: IgnorePointer( - child: _buildCursor(context), - ), - ), - ); - } - - Widget _buildCursor(BuildContext context) { - var color = widget.color; - if (widget.shouldBlink && !showCursor) { - color = Colors.transparent; - } - switch (widget.cursorStyle) { - case CursorStyle.verticalLine: - return Container( - color: color, - ); - case CursorStyle.borderLine: - return Container( - decoration: BoxDecoration( - border: Border.all(color: color, width: 2), - ), - ); - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/selectable.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/selectable.dart deleted file mode 100644 index c05d52940d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/selectable.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:flutter/material.dart'; - -enum CursorStyle { - verticalLine, - borderLine, -} - -/// [SelectableMixin] is used for the editor to calculate the position -/// and size of the selection. -/// -/// The widget returned by NodeWidgetBuilder must be with [SelectableMixin], -/// otherwise the [AppFlowySelectionService] will not work properly. -mixin SelectableMixin on State { - /// Returns the [Selection] surrounded by start and end - /// in current widget. - /// - /// [start] and [end] are the offsets under the global coordinate system. - /// - Selection getSelectionInRange(Offset start, Offset end); - - /// Returns a [List] of the [Rect] area within selection - /// in current widget. - /// - /// The return result must be a [List] of the [Rect] - /// under the local coordinate system. - List getRectsInSelection(Selection selection); - - /// Returns [Position] for the offset in current widget. - /// - /// [start] is the offset of the global coordination system. - Position getPositionInOffset(Offset start); - - /// Returns [Rect] for the position in current widget. - /// - /// The return result must be an offset of the local coordinate system. - Rect? getCursorRectInPosition(Position position) { - return null; - } - - /// Return global offset from local offset. - Offset localToGlobal(Offset offset); - - Position start(); - Position end(); - - /// For [TextNode] only. - /// - /// Only the widget rendered by [TextNode] need to implement the detail, - /// and the rest can return null. - TextSelection? getTextSelectionInSelection(Selection selection) => null; - - /// For [TextNode] only. - /// - /// Only the widget rendered by [TextNode] need to implement the detail, - /// and the rest can return null. - Selection? getWordBoundaryInOffset(Offset start) => null; - - /// For [TextNode] only. - /// - /// Only the widget rendered by [TextNode] need to implement the detail, - /// and the rest can return null. - Selection? getWordBoundaryInPosition(Position position) => null; - - bool get shouldCursorBlink => true; - - CursorStyle get cursorStyle => CursorStyle.verticalLine; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/selection_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/selection_widget.dart deleted file mode 100644 index e3dea7af34..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection/selection_widget.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; - -class SelectionWidget extends StatefulWidget { - const SelectionWidget({ - Key? key, - required this.layerLink, - required this.rect, - required this.color, - }) : super(key: key); - - final Color color; - final Rect rect; - final LayerLink layerLink; - - @override - State createState() => _SelectionWidgetState(); -} - -class _SelectionWidgetState extends State { - @override - Widget build(BuildContext context) { - return Positioned.fromRect( - rect: widget.rect, - child: CompositedTransformFollower( - link: widget.layerLink, - offset: widget.rect.topLeft, - showWhenUnlinked: true, - // Ignore the gestures in selection overlays - // to solve the problem that selection areas cannot overlap. - child: IgnorePointer( - child: Container( - color: widget.color, - ), - ), - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_item_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_item_widget.dart deleted file mode 100644 index 1d3a73f93e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_item_widget.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; -import 'package:flutter/material.dart'; - -class SelectionMenuItemWidget extends StatefulWidget { - const SelectionMenuItemWidget({ - Key? key, - required this.editorState, - required this.menuService, - required this.item, - required this.isSelected, - this.width = 140.0, - }) : super(key: key); - - final EditorState editorState; - final SelectionMenuService menuService; - final SelectionMenuItem item; - final double width; - final bool isSelected; - - @override - State createState() => - _SelectionMenuItemWidgetState(); -} - -class _SelectionMenuItemWidgetState extends State { - var _onHover = false; - - @override - Widget build(BuildContext context) { - final editorStyle = widget.editorState.editorStyle; - return Container( - padding: const EdgeInsets.fromLTRB(8.0, 5.0, 8.0, 5.0), - child: SizedBox( - width: widget.width, - child: TextButton.icon( - icon: widget.item - .icon(widget.editorState, widget.isSelected || _onHover), - style: ButtonStyle( - alignment: Alignment.centerLeft, - overlayColor: MaterialStateProperty.all( - editorStyle.selectionMenuItemSelectedColor), - backgroundColor: widget.isSelected - ? MaterialStateProperty.all( - editorStyle.selectionMenuItemSelectedColor) - : MaterialStateProperty.all(Colors.transparent), - ), - label: Text( - widget.item.name, - textAlign: TextAlign.left, - style: TextStyle( - color: (widget.isSelected || _onHover) - ? editorStyle.selectionMenuItemSelectedTextColor - : editorStyle.selectionMenuItemTextColor, - fontSize: 12.0, - ), - ), - onPressed: () { - widget.item - .handler(widget.editorState, widget.menuService, context); - }, - onHover: (value) { - setState(() { - _onHover = value; - }); - }, - ), - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart deleted file mode 100644 index 2d5af7bae1..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart +++ /dev/null @@ -1,253 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../core/legacy/built_in_attribute_keys.dart'; -import '../../editor_state.dart'; -import '../../infra/flowy_svg.dart'; -import '../../l10n/l10n.dart'; -import '../../service/default_text_operations/format_rich_text_style.dart'; -import '../image/image_upload_widget.dart'; -import 'selection_menu_widget.dart'; - -abstract class SelectionMenuService { - Offset get topLeft; - Offset get offset; - Alignment get alignment; - - void show(); - void dismiss(); -} - -class SelectionMenu implements SelectionMenuService { - SelectionMenu({ - required this.context, - required this.editorState, - }); - - final BuildContext context; - final EditorState editorState; - - OverlayEntry? _selectionMenuEntry; - bool _selectionUpdateByInner = false; - Offset? _topLeft; - Offset _offset = Offset.zero; - Alignment _alignment = Alignment.topLeft; - - @override - void dismiss() { - if (_selectionMenuEntry != null) { - editorState.service.keyboardService?.enable(); - editorState.service.scrollService?.enable(); - } - - _selectionMenuEntry?.remove(); - _selectionMenuEntry = null; - - // workaround: SelectionService has been released after hot reload. - final isSelectionDisposed = - editorState.service.selectionServiceKey.currentState == null; - if (!isSelectionDisposed) { - final selectionService = editorState.service.selectionService; - selectionService.currentSelection.removeListener(_onSelectionChange); - } - } - - @override - void show() { - dismiss(); - - final selectionService = editorState.service.selectionService; - final selectionRects = selectionService.selectionRects; - if (selectionRects.isEmpty) { - return; - } - // Workaround: We can customize the padding through the [EditorStyle], - // but the coordinates of overlay are not properly converted currently. - // Just subtract the padding here as a result. - const menuHeight = 200.0; - const menuOffset = Offset(0, 10); - final editorOffset = - editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; - final editorHeight = editorState.renderBox!.size.height; - - // show below default - var showBelow = true; - _alignment = Alignment.bottomLeft; - final bottomRight = selectionRects.first.bottomRight; - final topRight = selectionRects.first.topRight; - var offset = bottomRight + menuOffset; - // overflow - if (offset.dy + menuHeight >= editorOffset.dy + editorHeight) { - // show above - offset = topRight - menuOffset; - showBelow = false; - _alignment = Alignment.topLeft; - } - _topLeft = offset; - _offset = Offset(offset.dx, - showBelow ? offset.dy : MediaQuery.of(context).size.height - offset.dy); - - _selectionMenuEntry = OverlayEntry(builder: (context) { - return Positioned( - top: showBelow ? _offset.dy : null, - bottom: showBelow ? null : _offset.dy, - left: offset.dx, - right: 0, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SelectionMenuWidget( - items: [ - ..._defaultSelectionMenuItems, - ...editorState.selectionMenuItems, - ], - maxItemInRow: 5, - editorState: editorState, - menuService: this, - onExit: () { - dismiss(); - }, - onSelectionUpdate: () { - _selectionUpdateByInner = true; - }, - ), - ), - ); - }); - - Overlay.of(context)?.insert(_selectionMenuEntry!); - - editorState.service.keyboardService?.disable(showCursor: true); - editorState.service.scrollService?.disable(); - selectionService.currentSelection.addListener(_onSelectionChange); - } - - @override - Offset get topLeft { - return _topLeft ?? Offset.zero; - } - - @override - Alignment get alignment { - return _alignment; - } - - @override - Offset get offset { - return _offset; - } - - void _onSelectionChange() { - // workaround: SelectionService has been released after hot reload. - final isSelectionDisposed = - editorState.service.selectionServiceKey.currentState == null; - if (!isSelectionDisposed) { - final selectionService = editorState.service.selectionService; - if (selectionService.currentSelection.value == null) { - return; - } - } - - if (_selectionUpdateByInner) { - _selectionUpdateByInner = false; - return; - } - - dismiss(); - } -} - -@visibleForTesting -List get defaultSelectionMenuItems => - _defaultSelectionMenuItems; -final List _defaultSelectionMenuItems = [ - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.text, - icon: (editorState, onSelected) => - _selectionMenuIcon('text', editorState, onSelected), - keywords: ['text'], - handler: (editorState, _, __) { - insertTextNodeAfterSelection(editorState, {}); - }, - ), - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.heading1, - icon: (editorState, onSelected) => - _selectionMenuIcon('h1', editorState, onSelected), - keywords: ['heading 1, h1'], - handler: (editorState, _, __) { - insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h1); - }, - ), - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.heading2, - icon: (editorState, onSelected) => - _selectionMenuIcon('h2', editorState, onSelected), - keywords: ['heading 2, h2'], - handler: (editorState, _, __) { - insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h2); - }, - ), - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.heading3, - icon: (editorState, onSelected) => - _selectionMenuIcon('h3', editorState, onSelected), - keywords: ['heading 3, h3'], - handler: (editorState, _, __) { - insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h3); - }, - ), - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.image, - icon: (editorState, onSelected) => - _selectionMenuIcon('image', editorState, onSelected), - keywords: ['image'], - handler: showImageUploadMenu, - ), - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.bulletedList, - icon: (editorState, onSelected) => - _selectionMenuIcon('bulleted_list', editorState, onSelected), - keywords: ['bulleted list', 'list', 'unordered list'], - handler: (editorState, _, __) { - insertBulletedListAfterSelection(editorState); - }, - ), - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.numberedList, - icon: (editorState, onSelected) => - _selectionMenuIcon('number', editorState, onSelected), - keywords: ['numbered list', 'list', 'ordered list'], - handler: (editorState, _, __) { - insertNumberedListAfterSelection(editorState); - }, - ), - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.checkbox, - icon: (editorState, onSelected) => - _selectionMenuIcon('checkbox', editorState, onSelected), - keywords: ['todo list', 'list', 'checkbox list'], - handler: (editorState, _, __) { - insertCheckboxAfterSelection(editorState); - }, - ), - SelectionMenuItem( - name: AppFlowyEditorLocalizations.current.quote, - icon: (editorState, onSelected) => - _selectionMenuIcon('quote', editorState, onSelected), - keywords: ['quote', 'refer'], - handler: (editorState, _, __) { - insertQuoteAfterSelection(editorState); - }, - ), -]; - -Widget _selectionMenuIcon( - String name, EditorState editorState, bool onSelected) { - return FlowySvg( - name: 'selection_menu/$name', - color: onSelected - ? editorState.editorStyle.selectionMenuItemSelectedIconColor - : editorState.editorStyle.selectionMenuItemIconColor, - width: 18.0, - height: 18.0, - ); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart deleted file mode 100644 index e8a4eca357..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart +++ /dev/null @@ -1,390 +0,0 @@ -import 'dart:math'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -typedef SelectionMenuItemHandler = void Function( - EditorState editorState, - SelectionMenuService menuService, - BuildContext context, -); - -/// Selection Menu Item -class SelectionMenuItem { - SelectionMenuItem({ - required this.name, - required this.icon, - required this.keywords, - required SelectionMenuItemHandler handler, - }) { - this.handler = (editorState, menuService, context) { - _deleteSlash(editorState); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - handler(editorState, menuService, context); - }); - }; - } - - final String name; - final Widget Function(EditorState editorState, bool onSelected) icon; - - /// Customizes keywords for item. - /// - /// The keywords are used to quickly retrieve items. - final List keywords; - late final SelectionMenuItemHandler handler; - - void _deleteSlash(EditorState editorState) { - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final nodes = selectionService.currentSelectedNodes; - if (selection != null && nodes.length == 1) { - final node = nodes.first as TextNode; - final end = selection.start.offset; - final lastSlashIndex = - node.toPlainText().substring(0, end).lastIndexOf('/'); - // delete all the texts after '/' along with '/' - final transaction = editorState.transaction - ..deleteText( - node, - lastSlashIndex, - end - lastSlashIndex, - ); - - editorState.apply(transaction); - } - } - - /// Creates a selection menu entry for inserting a [Node]. - /// [name] and [iconData] define the appearance within the selection menu. - /// - /// The insert position is determined by the result of [replace] and - /// [insertBefore] - /// If no values are provided for [replace] and [insertBefore] the node is - /// inserted after the current selection. - /// [replace] takes precedence over [insertBefore] - /// - /// [updateSelection] can be used to update the selection after the node - /// has been inserted. - factory SelectionMenuItem.node({ - required String name, - required IconData iconData, - required List keywords, - required Node Function(EditorState editorState) nodeBuilder, - bool Function(EditorState editorState, TextNode textNode)? insertBefore, - bool Function(EditorState editorState, TextNode textNode)? replace, - Selection? Function( - EditorState editorState, - Path insertPath, - bool replaced, - bool insertedBefore, - )? - updateSelection, - }) { - return SelectionMenuItem( - name: name, - icon: (editorState, onSelected) => Icon( - iconData, - color: onSelected - ? editorState.editorStyle.selectionMenuItemSelectedIconColor - : editorState.editorStyle.selectionMenuItemIconColor, - size: 18.0, - ), - keywords: keywords, - handler: (editorState, _, __) { - final selection = - editorState.service.selectionService.currentSelection.value; - final textNodes = editorState - .service.selectionService.currentSelectedNodes - .whereType(); - if (textNodes.length != 1 || selection == null) { - return; - } - final textNode = textNodes.first; - final node = nodeBuilder(editorState); - final transaction = editorState.transaction; - final bReplace = replace?.call(editorState, textNode) ?? false; - final bInsertBefore = - insertBefore?.call(editorState, textNode) ?? false; - - //default insert after - var path = textNode.path.next; - if (bReplace) { - path = textNode.path; - } else if (bInsertBefore) { - path = textNode.path; - } - - transaction - ..insertNode(path, node) - ..afterSelection = updateSelection?.call( - editorState, path, bReplace, bInsertBefore) ?? - selection; - - if (bReplace) { - transaction.deleteNode(textNode); - } - - editorState.apply(transaction); - }, - ); - } -} - -class SelectionMenuWidget extends StatefulWidget { - const SelectionMenuWidget({ - Key? key, - required this.items, - required this.maxItemInRow, - required this.editorState, - required this.menuService, - required this.onExit, - required this.onSelectionUpdate, - }) : super(key: key); - - final List items; - final int maxItemInRow; - - final SelectionMenuService menuService; - final EditorState editorState; - - final VoidCallback onSelectionUpdate; - final VoidCallback onExit; - - @override - State createState() => _SelectionMenuWidgetState(); -} - -class _SelectionMenuWidgetState extends State { - final _focusNode = FocusNode(debugLabel: 'popup_list_widget'); - - int _selectedIndex = 0; - List _showingItems = []; - - String _keyword = ''; - String get keyword => _keyword; - set keyword(String newKeyword) { - _keyword = newKeyword; - - // Search items according to the keyword, and calculate the length of - // the longest keyword, which is used to dismiss the selection_service. - var maxKeywordLength = 0; - final items = widget.items - .where( - (item) => item.keywords.any((keyword) { - final value = keyword.contains(newKeyword.toLowerCase()); - if (value) { - maxKeywordLength = max(maxKeywordLength, keyword.length); - } - return value; - }), - ) - .toList(growable: false); - - Log.ui.debug('$items'); - - if (keyword.length >= maxKeywordLength + 2) { - widget.onExit(); - } else { - setState(() { - _showingItems = items; - }); - } - } - - @override - void initState() { - super.initState(); - - _showingItems = widget.items; - - WidgetsBinding.instance.addPostFrameCallback((_) { - _focusNode.requestFocus(); - }); - } - - @override - void dispose() { - _focusNode.dispose(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Focus( - focusNode: _focusNode, - onKey: _onKey, - child: Container( - decoration: BoxDecoration( - color: widget.editorState.editorStyle.selectionMenuBackgroundColor, - boxShadow: [ - BoxShadow( - blurRadius: 5, - spreadRadius: 1, - color: Colors.black.withOpacity(0.1), - ), - ], - borderRadius: BorderRadius.circular(6.0), - ), - child: _showingItems.isEmpty - ? _buildNoResultsWidget(context) - : _buildResultsWidget( - context, - _showingItems, - _selectedIndex, - ), - ), - ); - } - - Widget _buildResultsWidget( - BuildContext buildContext, - List items, - int selectedIndex, - ) { - List columns = []; - List itemWidgets = []; - for (var i = 0; i < items.length; i++) { - if (i != 0 && i % (widget.maxItemInRow) == 0) { - columns.add(Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: itemWidgets, - )); - itemWidgets = []; - } - itemWidgets.add(SelectionMenuItemWidget( - item: items[i], - isSelected: selectedIndex == i, - editorState: widget.editorState, - menuService: widget.menuService, - )); - } - if (itemWidgets.isNotEmpty) { - columns.add(Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: itemWidgets, - )); - itemWidgets = []; - } - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: columns, - ); - } - - Widget _buildNoResultsWidget(BuildContext context) { - return const Align( - alignment: Alignment.centerLeft, - child: Material( - child: Padding( - padding: EdgeInsets.all(12.0), - child: Text( - 'No results', - style: TextStyle(color: Colors.grey), - ), - ), - ), - ); - } - - /// Handles arrow keys to switch selected items - /// Handles keyword searches - /// Handles enter to select item and esc to exit - KeyEventResult _onKey(FocusNode node, RawKeyEvent event) { - Log.keyboard.debug('slash command, on key $event'); - if (event is! RawKeyDownEvent) { - return KeyEventResult.ignored; - } - - final arrowKeys = [ - LogicalKeyboardKey.arrowLeft, - LogicalKeyboardKey.arrowRight, - LogicalKeyboardKey.arrowUp, - LogicalKeyboardKey.arrowDown - ]; - - if (event.logicalKey == LogicalKeyboardKey.enter) { - if (0 <= _selectedIndex && _selectedIndex < _showingItems.length) { - _showingItems[_selectedIndex] - .handler(widget.editorState, widget.menuService, context); - return KeyEventResult.handled; - } - } else if (event.logicalKey == LogicalKeyboardKey.escape) { - widget.onExit(); - return KeyEventResult.handled; - } else if (event.logicalKey == LogicalKeyboardKey.backspace) { - if (keyword.isEmpty) { - widget.onExit(); - } else { - keyword = keyword.substring(0, keyword.length - 1); - } - _deleteLastCharacters(); - return KeyEventResult.handled; - } else if (event.character != null && - !arrowKeys.contains(event.logicalKey) && event.logicalKey != LogicalKeyboardKey.tab) { - keyword += event.character!; - _insertText(event.character!); - return KeyEventResult.handled; - } - - var newSelectedIndex = _selectedIndex; - if (event.logicalKey == LogicalKeyboardKey.arrowLeft) { - newSelectedIndex -= widget.maxItemInRow; - } else if (event.logicalKey == LogicalKeyboardKey.arrowRight) { - newSelectedIndex += widget.maxItemInRow; - } else if (event.logicalKey == LogicalKeyboardKey.arrowUp) { - newSelectedIndex -= 1; - } else if (event.logicalKey == LogicalKeyboardKey.arrowDown) { - newSelectedIndex += 1; - } else if (event.logicalKey == LogicalKeyboardKey.tab) { - newSelectedIndex += widget.maxItemInRow; - var currRow = (newSelectedIndex) % widget.maxItemInRow; - if (newSelectedIndex >= _showingItems.length) { - newSelectedIndex = (currRow + 1) % widget.maxItemInRow; - } - } - - if (newSelectedIndex != _selectedIndex) { - setState(() { - _selectedIndex = newSelectedIndex.clamp(0, _showingItems.length - 1); - }); - return KeyEventResult.handled; - } - return KeyEventResult.ignored; - } - - void _deleteLastCharacters({int length = 1}) { - final selectionService = widget.editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final nodes = selectionService.currentSelectedNodes; - if (selection != null && nodes.length == 1) { - widget.onSelectionUpdate(); - final transaction = widget.editorState.transaction - ..deleteText( - nodes.first as TextNode, - selection.start.offset - length, - length, - ); - widget.editorState.apply(transaction); - } - } - - void _insertText(String text) { - final selection = - widget.editorState.service.selectionService.currentSelection.value; - final nodes = - widget.editorState.service.selectionService.currentSelectedNodes; - if (selection != null && nodes.length == 1) { - widget.onSelectionUpdate(); - final transaction = widget.editorState.transaction - ..insertText( - nodes.first as TextNode, - selection.end.offset, - text, - ); - widget.editorState.apply(transaction); - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/style/editor_style.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/style/editor_style.dart deleted file mode 100644 index 0d252d1e04..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/style/editor_style.dart +++ /dev/null @@ -1,210 +0,0 @@ -import 'package:flutter/material.dart'; - -Iterable> get lightEditorStyleExtension => [ - EditorStyle.light, - ]; - -Iterable> get darkEditorStyleExtension => [ - EditorStyle.dark, - ]; - -class EditorStyle extends ThemeExtension { - // Editor styles - final EdgeInsets? padding; - final Color? backgroundColor; - final Color? cursorColor; - final Color? selectionColor; - - // Selection menu styles - final Color? selectionMenuBackgroundColor; - final Color? selectionMenuItemTextColor; - final Color? selectionMenuItemIconColor; - final Color? selectionMenuItemSelectedTextColor; - final Color? selectionMenuItemSelectedIconColor; - final Color? selectionMenuItemSelectedColor; - - // Text styles - final EdgeInsets? textPadding; - final TextStyle? textStyle; - final TextStyle? placeholderTextStyle; - final double lineHeight; - - // Rich text styles - final TextStyle? bold; - final TextStyle? italic; - final TextStyle? underline; - final TextStyle? strikethrough; - final TextStyle? href; - final TextStyle? code; - final String? highlightColorHex; - - EditorStyle({ - required this.padding, - required this.backgroundColor, - required this.cursorColor, - required this.selectionColor, - required this.selectionMenuBackgroundColor, - required this.selectionMenuItemTextColor, - required this.selectionMenuItemIconColor, - required this.selectionMenuItemSelectedTextColor, - required this.selectionMenuItemSelectedIconColor, - required this.selectionMenuItemSelectedColor, - required this.textPadding, - required this.textStyle, - required this.placeholderTextStyle, - required this.bold, - required this.italic, - required this.underline, - required this.strikethrough, - required this.href, - required this.code, - required this.highlightColorHex, - required this.lineHeight, - }); - - @override - EditorStyle copyWith({ - EdgeInsets? padding, - Color? backgroundColor, - Color? cursorColor, - Color? selectionColor, - Color? selectionMenuBackgroundColor, - Color? selectionMenuItemTextColor, - Color? selectionMenuItemIconColor, - Color? selectionMenuItemSelectedTextColor, - Color? selectionMenuItemSelectedIconColor, - Color? selectionMenuItemSelectedColor, - TextStyle? textStyle, - TextStyle? placeholderTextStyle, - TextStyle? bold, - TextStyle? italic, - TextStyle? underline, - TextStyle? strikethrough, - TextStyle? href, - TextStyle? code, - String? highlightColorHex, - double? lineHeight, - }) { - return EditorStyle( - padding: padding ?? this.padding, - backgroundColor: backgroundColor ?? this.backgroundColor, - cursorColor: cursorColor ?? this.cursorColor, - selectionColor: selectionColor ?? this.selectionColor, - selectionMenuBackgroundColor: - selectionMenuBackgroundColor ?? this.selectionMenuBackgroundColor, - selectionMenuItemTextColor: - selectionMenuItemTextColor ?? this.selectionMenuItemTextColor, - selectionMenuItemIconColor: - selectionMenuItemIconColor ?? this.selectionMenuItemIconColor, - selectionMenuItemSelectedTextColor: selectionMenuItemSelectedTextColor ?? - this.selectionMenuItemSelectedTextColor, - selectionMenuItemSelectedIconColor: selectionMenuItemSelectedIconColor ?? - this.selectionMenuItemSelectedIconColor, - selectionMenuItemSelectedColor: - selectionMenuItemSelectedColor ?? this.selectionMenuItemSelectedColor, - textPadding: textPadding ?? textPadding, - textStyle: textStyle ?? this.textStyle, - placeholderTextStyle: placeholderTextStyle ?? this.placeholderTextStyle, - bold: bold ?? this.bold, - italic: italic ?? this.italic, - underline: underline ?? this.underline, - strikethrough: strikethrough ?? this.strikethrough, - href: href ?? this.href, - code: code ?? this.code, - highlightColorHex: highlightColorHex ?? this.highlightColorHex, - lineHeight: lineHeight ?? this.lineHeight, - ); - } - - @override - ThemeExtension lerp( - ThemeExtension? other, double t) { - if (other == null || other is! EditorStyle) { - return this; - } - return EditorStyle( - padding: EdgeInsets.lerp(padding, other.padding, t), - backgroundColor: Color.lerp(backgroundColor, other.backgroundColor, t), - cursorColor: Color.lerp(cursorColor, other.cursorColor, t), - textPadding: EdgeInsets.lerp(textPadding, other.textPadding, t), - selectionColor: Color.lerp(selectionColor, other.selectionColor, t), - selectionMenuBackgroundColor: Color.lerp( - selectionMenuBackgroundColor, other.selectionMenuBackgroundColor, t), - selectionMenuItemTextColor: Color.lerp( - selectionMenuItemTextColor, other.selectionMenuItemTextColor, t), - selectionMenuItemIconColor: Color.lerp( - selectionMenuItemIconColor, other.selectionMenuItemIconColor, t), - selectionMenuItemSelectedTextColor: Color.lerp( - selectionMenuItemSelectedTextColor, - other.selectionMenuItemSelectedTextColor, - t), - selectionMenuItemSelectedIconColor: Color.lerp( - selectionMenuItemSelectedIconColor, - other.selectionMenuItemSelectedIconColor, - t), - selectionMenuItemSelectedColor: Color.lerp(selectionMenuItemSelectedColor, - other.selectionMenuItemSelectedColor, t), - textStyle: TextStyle.lerp(textStyle, other.textStyle, t), - placeholderTextStyle: - TextStyle.lerp(placeholderTextStyle, other.placeholderTextStyle, t), - bold: TextStyle.lerp(bold, other.bold, t), - italic: TextStyle.lerp(italic, other.italic, t), - underline: TextStyle.lerp(underline, other.underline, t), - strikethrough: TextStyle.lerp(strikethrough, other.strikethrough, t), - href: TextStyle.lerp(href, other.href, t), - code: TextStyle.lerp(code, other.code, t), - highlightColorHex: highlightColorHex, - lineHeight: lineHeight, - ); - } - - static EditorStyle? of(BuildContext context) { - return Theme.of(context).extension(); - } - - static final light = EditorStyle( - padding: const EdgeInsets.fromLTRB(200.0, 0.0, 200.0, 0.0), - backgroundColor: Colors.white, - cursorColor: const Color(0xFF00BCF0), - selectionColor: const Color.fromARGB(53, 111, 201, 231), - selectionMenuBackgroundColor: const Color(0xFFFFFFFF), - selectionMenuItemTextColor: const Color(0xFF333333), - selectionMenuItemIconColor: const Color(0xFF333333), - selectionMenuItemSelectedTextColor: const Color.fromARGB(255, 56, 91, 247), - selectionMenuItemSelectedIconColor: const Color.fromARGB(255, 56, 91, 247), - selectionMenuItemSelectedColor: const Color(0xFFE0F8FF), - textPadding: const EdgeInsets.symmetric(vertical: 8.0), - textStyle: const TextStyle(fontSize: 16.0, color: Colors.black), - placeholderTextStyle: const TextStyle(fontSize: 16.0, color: Colors.grey), - bold: const TextStyle(fontWeight: FontWeight.bold), - italic: const TextStyle(fontStyle: FontStyle.italic), - underline: const TextStyle(decoration: TextDecoration.underline), - strikethrough: const TextStyle(decoration: TextDecoration.lineThrough), - href: const TextStyle( - color: Colors.blue, - decoration: TextDecoration.underline, - ), - code: const TextStyle( - fontFamily: 'monospace', - color: Color(0xFF00BCF0), - backgroundColor: Color(0xFFE0F8FF), - ), - highlightColorHex: '0x6000BCF0', - lineHeight: 1.5, - ); - - static final dark = light.copyWith( - backgroundColor: Colors.black, - textStyle: const TextStyle(fontSize: 16.0, color: Colors.white), - placeholderTextStyle: TextStyle( - fontSize: 16.0, - color: Colors.white.withOpacity(0.3), - ), - selectionMenuBackgroundColor: const Color(0xFF282E3A), - selectionMenuItemTextColor: const Color(0xFFBBC3CD), - selectionMenuItemIconColor: const Color(0xFFBBC3CD), - selectionMenuItemSelectedTextColor: const Color(0xFF131720), - selectionMenuItemSelectedIconColor: const Color(0xFF131720), - selectionMenuItemSelectedColor: const Color(0xFF00BCF0), - ); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/style/plugin_styles.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/style/plugin_styles.dart deleted file mode 100644 index 19f70b2a92..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/style/plugin_styles.dart +++ /dev/null @@ -1,333 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/infra/flowy_svg.dart'; -import 'package:flutter/material.dart'; - -Iterable> get lightPluginStyleExtension => [ - HeadingPluginStyle.light, - CheckboxPluginStyle.light, - NumberListPluginStyle.light, - QuotedTextPluginStyle.light, - ]; - -Iterable> get darkPluginStyleExtension => [ - HeadingPluginStyle.dark, - CheckboxPluginStyle.dark, - NumberListPluginStyle.dark, - QuotedTextPluginStyle.dark, - BulletedListPluginStyle.dark, - ]; - -typedef TextStyleCustomizer = TextStyle Function( - EditorState editorState, TextNode textNode); -typedef PaddingCustomizer = EdgeInsets Function( - EditorState editorState, TextNode textNode); -typedef IconCustomizer = Widget Function( - EditorState editorState, TextNode textNode); - -class HeadingPluginStyle extends ThemeExtension { - const HeadingPluginStyle({ - required this.textStyle, - required this.padding, - }); - - final TextStyleCustomizer textStyle; - final PaddingCustomizer padding; - - @override - HeadingPluginStyle copyWith({ - TextStyleCustomizer? textStyle, - PaddingCustomizer? padding, - }) { - return HeadingPluginStyle( - textStyle: textStyle ?? this.textStyle, - padding: padding ?? this.padding, - ); - } - - @override - ThemeExtension lerp( - ThemeExtension? other, double t) { - if (other is! HeadingPluginStyle) { - return this; - } - return HeadingPluginStyle( - textStyle: other.textStyle, - padding: other.padding, - ); - } - - static final light = HeadingPluginStyle( - padding: (_, __) => const EdgeInsets.symmetric(vertical: 8.0), - textStyle: (editorState, textNode) { - final headingToFontSize = { - 'h1': 32.0, - 'h2': 28.0, - 'h3': 24.0, - 'h4': 18.0, - 'h5': 18.0, - 'h6': 18.0, - }; - final fontSize = headingToFontSize[textNode.attributes.heading] ?? 18.0; - return TextStyle( - fontSize: fontSize, - fontWeight: FontWeight.bold, - ); - }, - ); - - static final dark = light; -} - -class CheckboxPluginStyle extends ThemeExtension { - const CheckboxPluginStyle({ - required this.textStyle, - required this.padding, - required this.icon, - }); - - final TextStyleCustomizer textStyle; - final PaddingCustomizer padding; - final IconCustomizer icon; - - @override - CheckboxPluginStyle copyWith({ - TextStyleCustomizer? textStyle, - PaddingCustomizer? padding, - IconCustomizer? icon, - }) { - return CheckboxPluginStyle( - textStyle: textStyle ?? this.textStyle, - padding: padding ?? this.padding, - icon: icon ?? this.icon, - ); - } - - @override - ThemeExtension lerp( - ThemeExtension? other, double t) { - if (other is! CheckboxPluginStyle) { - return this; - } - return CheckboxPluginStyle( - textStyle: other.textStyle, - padding: other.padding, - icon: other.icon, - ); - } - - static final light = CheckboxPluginStyle( - padding: (_, __) => const EdgeInsets.symmetric(vertical: 8.0), - textStyle: (editorState, textNode) { - final isCheck = textNode.attributes.check; - return TextStyle( - decoration: isCheck ? TextDecoration.lineThrough : null, - color: isCheck ? Colors.grey.shade400 : null, - ); - }, - icon: (editorState, textNode) { - final isCheck = textNode.attributes.check; - const iconSize = Size.square(20.0); - const iconPadding = EdgeInsets.only(right: 5.0); - return FlowySvg( - width: iconSize.width, - height: iconSize.height, - padding: iconPadding, - name: isCheck ? 'check' : 'uncheck', - ); - }, - ); - - static final dark = light; -} - -class BulletedListPluginStyle extends ThemeExtension { - const BulletedListPluginStyle({ - required this.textStyle, - required this.padding, - required this.icon, - }); - - final TextStyleCustomizer textStyle; - final PaddingCustomizer padding; - final IconCustomizer icon; - - @override - BulletedListPluginStyle copyWith({ - TextStyleCustomizer? textStyle, - PaddingCustomizer? padding, - IconCustomizer? icon, - }) { - return BulletedListPluginStyle( - textStyle: textStyle ?? this.textStyle, - padding: padding ?? this.padding, - icon: icon ?? this.icon, - ); - } - - @override - ThemeExtension lerp( - ThemeExtension? other, double t) { - if (other is! BulletedListPluginStyle) { - return this; - } - return BulletedListPluginStyle( - textStyle: other.textStyle, - padding: other.padding, - icon: other.icon, - ); - } - - static final light = BulletedListPluginStyle( - padding: (_, __) => const EdgeInsets.symmetric(vertical: 8.0), - textStyle: (_, __) => const TextStyle(), - icon: (_, __) { - const iconSize = Size.square(20.0); - const iconPadding = EdgeInsets.only(right: 5.0); - return FlowySvg( - width: iconSize.width, - height: iconSize.height, - padding: iconPadding, - color: Colors.black, - name: 'point', - ); - }, - ); - - static final dark = light.copyWith(icon: (_, __) { - const iconSize = Size.square(20.0); - const iconPadding = EdgeInsets.only(right: 5.0); - return FlowySvg( - width: iconSize.width, - height: iconSize.height, - padding: iconPadding, - color: Colors.white, - name: 'point', - ); - }); -} - -class NumberListPluginStyle extends ThemeExtension { - const NumberListPluginStyle({ - required this.textStyle, - required this.padding, - required this.icon, - }); - - final TextStyleCustomizer textStyle; - final PaddingCustomizer padding; - final IconCustomizer icon; - - @override - NumberListPluginStyle copyWith({ - TextStyleCustomizer? textStyle, - PaddingCustomizer? padding, - IconCustomizer? icon, - }) { - return NumberListPluginStyle( - textStyle: textStyle ?? this.textStyle, - padding: padding ?? this.padding, - icon: icon ?? this.icon, - ); - } - - @override - ThemeExtension lerp( - ThemeExtension? other, - double t, - ) { - if (other is! NumberListPluginStyle) { - return this; - } - return NumberListPluginStyle( - textStyle: other.textStyle, - padding: other.padding, - icon: other.icon, - ); - } - - static final light = NumberListPluginStyle( - padding: (_, __) => const EdgeInsets.symmetric(vertical: 8.0), - textStyle: (_, __) => const TextStyle(), - icon: (_, textNode) { - const iconPadding = EdgeInsets.only(left: 5.0, right: 5.0); - return Container( - padding: iconPadding, - child: Text( - '${textNode.attributes.number.toString()}.', - style: const TextStyle( - fontSize: 16, - color: Colors.black, - ), - ), - ); - }, - ); - - static final dark = light.copyWith(icon: (editorState, textNode) { - const iconPadding = EdgeInsets.only(left: 5.0, right: 5.0); - return Container( - padding: iconPadding, - child: Text( - '${textNode.attributes.number.toString()}.', - style: const TextStyle( - fontSize: 16, - color: Colors.white, - ), - ), - ); - }); -} - -class QuotedTextPluginStyle extends ThemeExtension { - const QuotedTextPluginStyle({ - required this.textStyle, - required this.padding, - required this.icon, - }); - - final TextStyleCustomizer textStyle; - final PaddingCustomizer padding; - final IconCustomizer icon; - - @override - QuotedTextPluginStyle copyWith({ - TextStyleCustomizer? textStyle, - PaddingCustomizer? padding, - IconCustomizer? icon, - }) { - return QuotedTextPluginStyle( - textStyle: textStyle ?? this.textStyle, - padding: padding ?? this.padding, - icon: icon ?? this.icon, - ); - } - - @override - ThemeExtension lerp( - ThemeExtension? other, double t) { - if (other is! QuotedTextPluginStyle) { - return this; - } - return QuotedTextPluginStyle( - textStyle: other.textStyle, - padding: other.padding, - icon: other.icon, - ); - } - - static final light = QuotedTextPluginStyle( - padding: (_, __) => const EdgeInsets.symmetric(vertical: 8.0), - textStyle: (_, __) => const TextStyle(), - icon: (_, __) { - const iconSize = Size.square(20.0); - const iconPadding = EdgeInsets.only(right: 5.0); - return FlowySvg( - width: iconSize.width, - padding: iconPadding, - name: 'quote', - ); - }, - ); - - static final dark = light; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart deleted file mode 100644 index b4df31fca3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart +++ /dev/null @@ -1,706 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; -import 'package:appflowy_editor/src/flutter/overlay.dart'; -import 'package:appflowy_editor/src/infra/clipboard.dart'; -import 'package:appflowy_editor/src/infra/flowy_svg.dart'; -import 'package:appflowy_editor/src/render/color_menu/color_picker.dart'; -import 'package:appflowy_editor/src/render/link_menu/link_menu.dart'; -import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; -import 'package:appflowy_editor/src/extensions/editor_state_extensions.dart'; -import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; -import 'dart:io' show Platform; - -import 'package:flutter/material.dart' hide Overlay, OverlayEntry; - -typedef ToolbarItemEventHandler = void Function( - EditorState editorState, BuildContext context); -typedef ToolbarItemValidator = bool Function(EditorState editorState); -typedef ToolbarItemHighlightCallback = bool Function(EditorState editorState); - -class ToolbarItem { - ToolbarItem({ - required this.id, - required this.type, - this.tooltipsMessage = '', - this.iconBuilder, - required this.validator, - this.highlightCallback, - this.handler, - this.itemBuilder, - }) { - assert( - (iconBuilder != null && itemBuilder == null) || - (iconBuilder == null && itemBuilder != null), - 'iconBuilder and itemBuilder must be set one of them', - ); - } - - final String id; - final int type; - final String tooltipsMessage; - final ToolbarItemValidator validator; - - final Widget Function(bool isHighlight)? iconBuilder; - final ToolbarItemEventHandler? handler; - final ToolbarItemHighlightCallback? highlightCallback; - - final Widget Function(BuildContext context, EditorState editorState)? - itemBuilder; - - factory ToolbarItem.divider() { - return ToolbarItem( - id: 'divider', - type: -1, - iconBuilder: (_) => const FlowySvg(name: 'toolbar/divider'), - validator: (editorState) => true, - handler: (editorState, context) {}, - highlightCallback: (editorState) => false, - ); - } - - @override - bool operator ==(Object other) { - if (other is! ToolbarItem) { - return false; - } - if (identical(this, other)) { - return true; - } - return id == other.id; - } - - @override - int get hashCode => id.hashCode; -} - -List defaultToolbarItems = [ - ToolbarItem( - id: 'appflowy.toolbar.h1', - type: 1, - tooltipsMessage: AppFlowyEditorLocalizations.current.heading1, - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/h1', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _onlyShowInSingleTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.heading, - (value) => value == BuiltInAttributeKey.h1, - ), - handler: (editorState, context) => - formatHeading(editorState, BuiltInAttributeKey.h1), - ), - ToolbarItem( - id: 'appflowy.toolbar.h2', - type: 1, - tooltipsMessage: AppFlowyEditorLocalizations.current.heading2, - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/h2', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _onlyShowInSingleTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.heading, - (value) => value == BuiltInAttributeKey.h2, - ), - handler: (editorState, context) => - formatHeading(editorState, BuiltInAttributeKey.h2), - ), - ToolbarItem( - id: 'appflowy.toolbar.h3', - type: 1, - tooltipsMessage: AppFlowyEditorLocalizations.current.heading3, - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/h3', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _onlyShowInSingleTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.heading, - (value) => value == BuiltInAttributeKey.h3, - ), - handler: (editorState, context) => - formatHeading(editorState, BuiltInAttributeKey.h3), - ), - ToolbarItem( - id: 'appflowy.toolbar.bold', - type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.bold + - "\n" + - (Platform.isMacOS ? "⌘ + B" : "CTRL + B"), - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/bold', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _showInBuiltInTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.bold, - (value) => value == true, - ), - handler: (editorState, context) => formatBold(editorState), - ), - ToolbarItem( - id: 'appflowy.toolbar.italic', - type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.italic + - "\n" + - (Platform.isMacOS ? "⌘ + I" : "CTRL + I"), - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/italic', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _showInBuiltInTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.italic, - (value) => value == true, - ), - handler: (editorState, context) => formatItalic(editorState), - ), - ToolbarItem( - id: 'appflowy.toolbar.underline', - type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.underline + - "\n" + - (Platform.isMacOS ? "⌘ + U" : "CTRL + U"), - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/underline', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _showInBuiltInTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.underline, - (value) => value == true, - ), - handler: (editorState, context) => formatUnderline(editorState), - ), - ToolbarItem( - id: 'appflowy.toolbar.strikethrough', - type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.strikethrough + - "\n" + - (Platform.isMacOS ? "⌘ + SHIFT + S" : "CTRL + SHIFT + S"), - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/strikethrough', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _showInBuiltInTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.strikethrough, - (value) => value == true, - ), - handler: (editorState, context) => formatStrikethrough(editorState), - ), - ToolbarItem( - id: 'appflowy.toolbar.code', - type: 2, - tooltipsMessage: AppFlowyEditorLocalizations.current.embedCode + - "\n" + - (Platform.isMacOS ? "⌘ + E" : "CTRL + E"), - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/code', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _showInBuiltInTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.code, - (value) => value == true, - ), - handler: (editorState, context) => formatEmbedCode(editorState), - ), - ToolbarItem( - id: 'appflowy.toolbar.quote', - type: 3, - tooltipsMessage: AppFlowyEditorLocalizations.current.quote, - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/quote', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _onlyShowInSingleTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.subtype, - (value) => value == BuiltInAttributeKey.quote, - ), - handler: (editorState, context) { - formatQuote(editorState); - }, - ), - ToolbarItem( - id: 'appflowy.toolbar.bulleted_list', - type: 3, - tooltipsMessage: AppFlowyEditorLocalizations.current.bulletedList, - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/bulleted_list', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _onlyShowInSingleTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.subtype, - (value) => value == BuiltInAttributeKey.bulletedList, - ), - handler: (editorState, context) => formatBulletedList(editorState), - ), - ToolbarItem( - id: 'appflowy.toolbar.number_list', - type: 3, - tooltipsMessage: AppFlowyEditorLocalizations.current.numberedList, - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/number_list', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _onlyShowInSingleTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.subtype, - (value) => value == BuiltInAttributeKey.numberList, - ), - handler: (editorState, context) => formatNumberedList(editorState), - ), - ToolbarItem( - id: 'appflowy.toolbar.link', - type: 4, - tooltipsMessage: AppFlowyEditorLocalizations.current.link + - "\n" + - (Platform.isMacOS ? "⌘ + K" : "CTRL + K"), - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/link', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _onlyShowInSingleTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.href, - (value) => value != null, - ), - handler: (editorState, context) => showLinkMenu(context, editorState), - ), - ToolbarItem( - id: 'appflowy.toolbar.highlight', - type: 4, - tooltipsMessage: AppFlowyEditorLocalizations.current.highlight + - "\n" + - (Platform.isMacOS ? "⌘ + SHIFT + H" : "CTRL + SHIFT + H"), - iconBuilder: (isHighlight) => FlowySvg( - name: 'toolbar/highlight', - color: isHighlight ? Colors.lightBlue : null, - ), - validator: _showInBuiltInTextSelection, - highlightCallback: (editorState) => _allSatisfy( - editorState, - BuiltInAttributeKey.backgroundColor, - (value) { - return value != null && value != '0x00000000'; // transparent color; - }, - ), - handler: (editorState, context) => formatHighlight( - editorState, - editorState.editorStyle.highlightColorHex!, - ), - ), - ToolbarItem( - id: 'appflowy.toolbar.color', - type: 4, - tooltipsMessage: AppFlowyEditorLocalizations.current.color, - iconBuilder: (isHighlight) => Icon( - Icons.color_lens_outlined, - size: 14, - color: isHighlight ? Colors.lightBlue : Colors.white, - ), - validator: _showInBuiltInTextSelection, - highlightCallback: (editorState) => - _allSatisfy( - editorState, - BuiltInAttributeKey.color, - (value) => - value != null && - value != _generateFontColorOptions(editorState).first.colorHex, - ) || - _allSatisfy( - editorState, - BuiltInAttributeKey.backgroundColor, - (value) => - value != null && - value != - _generateBackgroundColorOptions(editorState).first.colorHex, - ), - handler: (editorState, context) => showColorMenu( - context, - editorState, - ), - ), -]; - -ToolbarItemValidator _onlyShowInSingleTextSelection = (editorState) { - final result = _showInBuiltInTextSelection(editorState); - if (!result) { - return false; - } - final nodes = editorState.service.selectionService.currentSelectedNodes; - return (nodes.length == 1 && nodes.first is TextNode); -}; - -ToolbarItemValidator _showInBuiltInTextSelection = (editorState) { - final nodes = editorState.service.selectionService.currentSelectedNodes - .whereType() - .where( - (textNode) => - BuiltInAttributeKey.globalStyleKeys.contains(textNode.subtype) || - textNode.subtype == null, - ); - return nodes.isNotEmpty; -}; - -bool _allSatisfy( - EditorState editorState, - String styleKey, - bool Function(dynamic value) test, -) { - final selection = editorState.service.selectionService.currentSelection.value; - return selection != null && - editorState.selectedTextNodes.allSatisfyInSelection( - selection, - styleKey, - test, - ); -} - -OverlayEntry? _linkMenuOverlay; -OverlayEntry? _colorMenuOverlay; - -EditorState? _editorState; -bool _changeSelectionInner = false; -void showLinkMenu( - BuildContext context, - EditorState editorState, { - Selection? customSelection, -}) { - final rects = editorState.service.selectionService.selectionRects; - var maxBottom = 0.0; - late Rect matchRect; - for (final rect in rects) { - if (rect.bottom > maxBottom) { - maxBottom = rect.bottom; - matchRect = rect; - } - } - final baseOffset = - editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; - matchRect = matchRect.shift(-baseOffset); - - _dismissLinkMenu(); - _editorState = editorState; - - // Since the link menu will only show in single text selection, - // We get the text node directly instead of judging details again. - final selection = customSelection ?? - editorState.service.selectionService.currentSelection.value; - final node = editorState.service.selectionService.currentSelectedNodes; - if (selection == null || node.isEmpty || node.first is! TextNode) { - return; - } - final index = - selection.isBackward ? selection.start.offset : selection.end.offset; - final length = (selection.start.offset - selection.end.offset).abs(); - final textNode = node.first as TextNode; - String? linkText; - if (textNode.allSatisfyLinkInSelection(selection)) { - linkText = textNode.getAttributeInSelection( - selection, - BuiltInAttributeKey.href, - ); - } - - _linkMenuOverlay = OverlayEntry(builder: (context) { - return Positioned( - top: matchRect.bottom + 5.0, - left: matchRect.left, - child: Material( - child: LinkMenu( - linkText: linkText, - editorState: editorState, - onOpenLink: () async { - await safeLaunchUrl(linkText); - }, - onSubmitted: (text) async { - await editorState.formatLinkInText( - editorState, - text, - textNode: textNode, - ); - - _dismissLinkMenu(); - }, - onCopyLink: () { - AppFlowyClipboard.setData(text: linkText); - _dismissLinkMenu(); - }, - onRemoveLink: () { - final transaction = editorState.transaction - ..formatText( - textNode, - index, - length, - {BuiltInAttributeKey.href: null}, - ); - editorState.apply(transaction); - _dismissLinkMenu(); - }, - onFocusChange: (value) { - if (value && customSelection != null) { - _changeSelectionInner = true; - editorState.service.selectionService - .updateSelection(customSelection); - } - }, - ), - ), - ); - }); - Overlay.of(context)?.insert(_linkMenuOverlay!); - - editorState.service.scrollService?.disable(); - editorState.service.keyboardService?.disable(); - editorState.service.selectionService.currentSelection - .addListener(_dismissLinkMenu); -} - -void _dismissLinkMenu() { - // workaround: SelectionService has been released after hot reload. - final isSelectionDisposed = - _editorState?.service.selectionServiceKey.currentState == null; - if (isSelectionDisposed) { - return; - } - if (_editorState?.service.selectionService.currentSelection.value == null) { - return; - } - if (_changeSelectionInner) { - _changeSelectionInner = false; - return; - } - _linkMenuOverlay?.remove(); - _linkMenuOverlay = null; - - _editorState?.service.scrollService?.enable(); - _editorState?.service.keyboardService?.enable(); - _editorState?.service.selectionService.currentSelection - .removeListener(_dismissLinkMenu); - _editorState = null; -} - -void _dismissColorMenu() { - // workaround: SelectionService has been released after hot reload. - final isSelectionDisposed = - _editorState?.service.selectionServiceKey.currentState == null; - if (isSelectionDisposed) { - return; - } - if (_editorState?.service.selectionService.currentSelection.value == null) { - return; - } - if (_changeSelectionInner) { - _changeSelectionInner = false; - return; - } - _colorMenuOverlay?.remove(); - _colorMenuOverlay = null; - - _editorState?.service.scrollService?.enable(); - _editorState?.service.keyboardService?.enable(); - _editorState?.service.selectionService.currentSelection - .removeListener(_dismissColorMenu); - _editorState = null; -} - -void showColorMenu( - BuildContext context, - EditorState editorState, { - Selection? customSelection, -}) { - final rects = editorState.service.selectionService.selectionRects; - var maxBottom = 0.0; - late Rect matchRect; - for (final rect in rects) { - if (rect.bottom > maxBottom) { - maxBottom = rect.bottom; - matchRect = rect; - } - } - final baseOffset = - editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; - matchRect = matchRect.shift(-baseOffset); - - _dismissColorMenu(); - _editorState = editorState; - - // Since the link menu will only show in single text selection, - // We get the text node directly instead of judging details again. - final selection = customSelection ?? - editorState.service.selectionService.currentSelection.value; - - final node = editorState.service.selectionService.currentSelectedNodes; - if (selection == null || node.isEmpty || node.first is! TextNode) { - return; - } - final textNode = node.first as TextNode; - - String? backgroundColorHex; - if (textNode.allSatisfyBackgroundColorInSelection(selection)) { - backgroundColorHex = textNode.getAttributeInSelection( - selection, - BuiltInAttributeKey.backgroundColor, - ); - } - String? fontColorHex; - if (textNode.allSatisfyFontColorInSelection(selection)) { - fontColorHex = textNode.getAttributeInSelection( - selection, - BuiltInAttributeKey.color, - ); - } else { - fontColorHex = editorState.editorStyle.textStyle?.color?.toHex(); - } - - final style = editorState.editorStyle; - _colorMenuOverlay = OverlayEntry(builder: (context) { - return Positioned( - top: matchRect.bottom + 5.0, - left: matchRect.left + 10, - child: Material( - color: Colors.transparent, - child: ColorPicker( - pickerBackgroundColor: - style.selectionMenuBackgroundColor ?? Colors.white, - pickerItemHoverColor: style.selectionMenuItemSelectedColor ?? - Colors.blue.withOpacity(0.3), - pickerItemTextColor: style.selectionMenuItemTextColor ?? Colors.black, - selectedFontColorHex: fontColorHex, - selectedBackgroundColorHex: backgroundColorHex, - fontColorOptions: _generateFontColorOptions(editorState), - backgroundColorOptions: _generateBackgroundColorOptions(editorState), - onSubmittedbackgroundColorHex: (color) { - formatHighlightColor( - editorState, - color, - ); - _dismissColorMenu(); - }, - onSubmittedFontColorHex: (color) { - formatFontColor( - editorState, - color, - ); - _dismissColorMenu(); - }, - ), - ), - ); - }); - Overlay.of(context)?.insert(_colorMenuOverlay!); - - editorState.service.scrollService?.disable(); - editorState.service.keyboardService?.disable(); - editorState.service.selectionService.currentSelection - .addListener(_dismissColorMenu); -} - -List _generateFontColorOptions(EditorState editorState) { - final defaultColor = - editorState.editorStyle.textStyle?.color ?? Colors.black; // black - return [ - ColorOption( - colorHex: defaultColor.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorDefault, - ), - ColorOption( - colorHex: Colors.grey.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorGray, - ), - ColorOption( - colorHex: Colors.brown.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorBrown, - ), - ColorOption( - colorHex: Colors.yellow.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorYellow, - ), - ColorOption( - colorHex: Colors.green.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorGreen, - ), - ColorOption( - colorHex: Colors.blue.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorBlue, - ), - ColorOption( - colorHex: Colors.purple.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorPurple, - ), - ColorOption( - colorHex: Colors.pink.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorPink, - ), - ColorOption( - colorHex: Colors.red.toHex(), - name: AppFlowyEditorLocalizations.current.fontColorRed, - ), - ]; -} - -List _generateBackgroundColorOptions(EditorState editorState) { - final defaultBackgroundColorHex = - editorState.editorStyle.highlightColorHex ?? '0x6000BCF0'; - return [ - ColorOption( - colorHex: defaultBackgroundColorHex, - name: AppFlowyEditorLocalizations.current.backgroundColorDefault, - ), - ColorOption( - colorHex: Colors.grey.withOpacity(0.3).toHex(), - name: AppFlowyEditorLocalizations.current.backgroundColorGray, - ), - ColorOption( - colorHex: Colors.brown.withOpacity(0.3).toHex(), - name: AppFlowyEditorLocalizations.current.backgroundColorBrown, - ), - ColorOption( - colorHex: Colors.yellow.withOpacity(0.3).toHex(), - name: AppFlowyEditorLocalizations.current.backgroundColorYellow, - ), - ColorOption( - colorHex: Colors.green.withOpacity(0.3).toHex(), - name: AppFlowyEditorLocalizations.current.backgroundColorGreen, - ), - ColorOption( - colorHex: Colors.blue.withOpacity(0.3).toHex(), - name: AppFlowyEditorLocalizations.current.backgroundColorBlue, - ), - ColorOption( - colorHex: Colors.purple.withOpacity(0.3).toHex(), - name: AppFlowyEditorLocalizations.current.backgroundColorPurple, - ), - ColorOption( - colorHex: Colors.pink.withOpacity(0.3).toHex(), - name: AppFlowyEditorLocalizations.current.backgroundColorPink, - ), - ColorOption( - colorHex: Colors.red.withOpacity(0.3).toHex(), - name: AppFlowyEditorLocalizations.current.backgroundColorRed, - ), - ]; -} - -extension on Color { - String toHex() { - return '0x${value.toRadixString(16)}'; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item_widget.dart deleted file mode 100644 index 22bcfab54e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item_widget.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'toolbar_item.dart'; - -class ToolbarItemWidget extends StatelessWidget { - const ToolbarItemWidget({ - Key? key, - required this.item, - required this.isHighlight, - required this.onPressed, - }) : super(key: key); - - final ToolbarItem item; - final VoidCallback onPressed; - final bool isHighlight; - - @override - Widget build(BuildContext context) { - if (item.iconBuilder != null) { - return SizedBox( - width: 28, - height: 28, - child: Tooltip( - textAlign: TextAlign.center, - preferBelow: false, - message: item.tooltipsMessage, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: IconButton( - hoverColor: Colors.transparent, - highlightColor: Colors.transparent, - padding: EdgeInsets.zero, - icon: item.iconBuilder!(isHighlight), - iconSize: 28, - onPressed: onPressed, - ), - ), - ), - ); - } - return const SizedBox.shrink(); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_widget.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_widget.dart deleted file mode 100644 index fc9df7346f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/toolbar/toolbar_widget.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:appflowy_editor/src/flutter/overlay.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; -import 'package:flutter/material.dart' hide Overlay, OverlayEntry; - -import 'package:appflowy_editor/src/editor_state.dart'; - -mixin ToolbarMixin on State { - void hide(); -} - -class ToolbarWidget extends StatefulWidget { - const ToolbarWidget({ - Key? key, - required this.editorState, - required this.layerLink, - required this.offset, - required this.items, - this.alignment = Alignment.topLeft, - }) : super(key: key); - - final EditorState editorState; - final LayerLink layerLink; - final Offset offset; - final List items; - final Alignment alignment; - - @override - State createState() => _ToolbarWidgetState(); -} - -class _ToolbarWidgetState extends State with ToolbarMixin { - OverlayEntry? _listToolbarOverlay; - - @override - Widget build(BuildContext context) { - return Positioned( - top: widget.offset.dx, - left: widget.offset.dy, - child: CompositedTransformFollower( - link: widget.layerLink, - showWhenUnlinked: true, - offset: widget.offset, - followerAnchor: widget.alignment, - child: _buildToolbar(context), - ), - ); - } - - @override - void hide() { - _listToolbarOverlay?.remove(); - _listToolbarOverlay = null; - } - - Widget _buildToolbar(BuildContext context) { - return Material( - borderRadius: BorderRadius.circular(8.0), - color: const Color(0xFF333333), - child: Padding( - padding: const EdgeInsets.only(left: 8.0, right: 8.0), - child: SizedBox( - height: 32.0, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: widget.items - .map( - (item) => Center( - child: - item.itemBuilder?.call(context, widget.editorState) ?? - ToolbarItemWidget( - item: item, - isHighlight: item.highlightCallback - ?.call(widget.editorState) ?? - false, - onPressed: () { - item.handler?.call(widget.editorState, context); - widget.editorState.service.keyboardService - ?.enable(); - }, - ), - ), - ) - .toList(growable: false), - ), - ), - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/context_menu/built_in_context_menu_item.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/context_menu/built_in_context_menu_item.dart deleted file mode 100644 index b850f78e9a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/context_menu/built_in_context_menu_item.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:appflowy_editor/src/service/context_menu/context_menu.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/copy_paste_handler.dart'; - -final builtInContextMenuItems = [ - [ - // cut - ContextMenuItem( - name: 'Cut', - onPressed: (editorState) { - cutEventHandler(editorState, null); - }, - ), - // copy - ContextMenuItem( - name: 'Copy', - onPressed: (editorState) { - copyEventHandler(editorState, null); - }, - ), - // Paste - ContextMenuItem( - name: 'Paste', - onPressed: (editorState) { - pasteEventHandler(editorState, null); - }, - ), - ], -]; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/context_menu/context_menu.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/context_menu/context_menu.dart deleted file mode 100644 index 92b43abdbb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/context_menu/context_menu.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:flutter/material.dart'; - -class ContextMenuItem { - ContextMenuItem({ - required this.name, - required this.onPressed, - }); - - final String name; - final void Function(EditorState editorState) onPressed; -} - -class ContextMenu extends StatelessWidget { - const ContextMenu({ - Key? key, - required this.position, - required this.editorState, - required this.items, - required this.onPressed, - }) : super(key: key); - - final Offset position; - final EditorState editorState; - final List> items; - final VoidCallback onPressed; - - @override - Widget build(BuildContext context) { - final children = []; - for (var i = 0; i < items.length; i++) { - for (var j = 0; j < items[i].length; j++) { - var onHover = false; - children.add( - StatefulBuilder( - builder: (BuildContext context, setState) { - return Material( - color: editorState.editorStyle.selectionMenuBackgroundColor, - child: InkWell( - hoverColor: - editorState.editorStyle.selectionMenuItemSelectedColor, - customBorder: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(6), - ), - onTap: () { - items[i][j].onPressed(editorState); - onPressed(); - }, - onHover: (value) => setState(() { - onHover = value; - }), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - items[i][j].name, - textAlign: TextAlign.start, - style: TextStyle( - fontSize: 14, - color: onHover - ? editorState - .editorStyle.selectionMenuItemSelectedTextColor - : editorState - .editorStyle.selectionMenuItemTextColor, - ), - ), - ), - ), - ); - }, - ), - ); - } - if (i != items.length - 1) { - children.add(const Divider()); - } - } - - return Positioned( - top: position.dy, - left: position.dx, - child: Container( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4), - constraints: const BoxConstraints( - minWidth: 140, - ), - decoration: BoxDecoration( - color: editorState.editorStyle.selectionMenuBackgroundColor, - boxShadow: [ - BoxShadow( - blurRadius: 5, - spreadRadius: 1, - color: Colors.black.withOpacity(0.1), - ), - ], - borderRadius: BorderRadius.circular(6.0), - ), - child: IntrinsicWidth( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: children, - ), - ), - ), - ); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart deleted file mode 100644 index 0141637b1c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart +++ /dev/null @@ -1,282 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; - -void insertHeadingAfterSelection(EditorState editorState, String heading) { - insertTextNodeAfterSelection(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: heading, - }); -} - -void insertQuoteAfterSelection(EditorState editorState) { - insertTextNodeAfterSelection(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }); -} - -void insertCheckboxAfterSelection(EditorState editorState) { - insertTextNodeAfterSelection(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }); -} - -void insertBulletedListAfterSelection(EditorState editorState) { - insertTextNodeAfterSelection(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }); -} - -void insertNumberedListAfterSelection(EditorState editorState) { - insertTextNodeAfterSelection(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.numberList, - BuiltInAttributeKey.number: 1, - }); -} - -bool insertTextNodeAfterSelection( - EditorState editorState, Attributes attributes) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - if (selection == null || nodes.isEmpty) { - return false; - } - - final node = nodes.first; - if (node is TextNode && node.delta.isEmpty) { - formatTextNodes(editorState, attributes); - } else { - final next = selection.end.path.next; - final transaction = editorState.transaction - ..insertNode( - next, - TextNode.empty(attributes: attributes), - ) - ..afterSelection = Selection.collapsed( - Position(path: next, offset: 0), - ); - editorState.apply(transaction); - } - - return true; -} - -void formatText(EditorState editorState) { - formatTextNodes(editorState, {}); -} - -void formatHeading(EditorState editorState, String heading) { - formatTextNodes(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: heading, - }); -} - -void formatQuote(EditorState editorState) { - formatTextNodes(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }); -} - -void formatCheckbox(EditorState editorState, bool check) { - formatTextNodes(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: check, - }); -} - -void formatBulletedList(EditorState editorState) { - formatTextNodes(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }); -} - -void formatNumberedList(EditorState editorState) { - formatTextNodes(editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.numberList, - BuiltInAttributeKey.number: 1, - }); -} - -/// Format the current selection with the given attributes. -/// -/// If the selected nodes are not text nodes, this method will do nothing. -/// If the selected text nodes already contain the style in attributes, this method will remove the existing style. -bool formatTextNodes(EditorState editorState, Attributes attributes) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(); - - if (textNodes.isEmpty) { - return false; - } - - final transaction = editorState.transaction; - - for (final textNode in textNodes) { - var newAttributes = {...textNode.attributes}; - for (final globalStyleKey in BuiltInAttributeKey.globalStyleKeys) { - if (newAttributes.keys.contains(globalStyleKey)) { - newAttributes[globalStyleKey] = null; - } - } - - // if an attribute already exists in the node, it should be removed instead - for (final entry in attributes.entries) { - if (textNode.attributes.containsKey(entry.key) && - textNode.attributes[entry.key] == entry.value) { - // attribute is not added to the node new attributes - } else { - newAttributes.addEntries([entry]); - } - } - transaction - ..updateNode( - textNode, - newAttributes, - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: textNode.toPlainText().length, - ), - ); - } - - editorState.apply(transaction); - return true; -} - -bool formatBold(EditorState editorState) { - return formatRichTextPartialStyle(editorState, BuiltInAttributeKey.bold); -} - -bool formatItalic(EditorState editorState) { - return formatRichTextPartialStyle(editorState, BuiltInAttributeKey.italic); -} - -bool formatUnderline(EditorState editorState) { - return formatRichTextPartialStyle(editorState, BuiltInAttributeKey.underline); -} - -bool formatStrikethrough(EditorState editorState) { - return formatRichTextPartialStyle( - editorState, BuiltInAttributeKey.strikethrough); -} - -bool formatEmbedCode(EditorState editorState) { - return formatRichTextPartialStyle(editorState, BuiltInAttributeKey.code); -} - -bool formatHighlight(EditorState editorState, String colorHex) { - bool value = _allSatisfyInSelection( - editorState, - BuiltInAttributeKey.backgroundColor, - colorHex, - ); - return formatRichTextPartialStyle( - editorState, - BuiltInAttributeKey.backgroundColor, - customValue: value ? '0x00000000' : colorHex, - ); -} - -bool formatHighlightColor(EditorState editorState, String colorHex) { - return formatRichTextPartialStyle( - editorState, - BuiltInAttributeKey.backgroundColor, - customValue: colorHex, - ); -} - -bool formatFontColor(EditorState editorState, String colorHex) { - return formatRichTextPartialStyle( - editorState, - BuiltInAttributeKey.color, - customValue: colorHex, - ); -} - -bool formatRichTextPartialStyle(EditorState editorState, String styleKey, - {Object? customValue}) { - Attributes attributes = { - styleKey: customValue ?? - !_allSatisfyInSelection( - editorState, - styleKey, - customValue ?? true, - ), - }; - - return formatRichTextStyle(editorState, attributes); -} - -bool _allSatisfyInSelection( - EditorState editorState, - String styleKey, - dynamic matchValue, -) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(growable: false); - - if (selection == null || textNodes.isEmpty) { - return false; - } - - return textNodes.allSatisfyInSelection(selection, styleKey, (value) { - return value == matchValue; - }); -} - -bool formatRichTextStyle(EditorState editorState, Attributes attributes) { - var selection = editorState.service.selectionService.currentSelection.value; - var nodes = editorState.service.selectionService.currentSelectedNodes; - - if (selection == null) { - return false; - } - - nodes = selection.isBackward ? nodes : nodes.reversed.toList(growable: false); - selection = selection.isBackward ? selection : selection.reversed; - - var textNodes = nodes.whereType().toList(); - if (textNodes.isEmpty) { - return false; - } - - final transaction = editorState.transaction; - - // 1. All nodes are text nodes. - // 2. The first node is not TextNode. - // 3. The last node is not TextNode. - if (nodes.length == textNodes.length && textNodes.length == 1) { - transaction.formatText( - textNodes.first, - selection.start.offset, - selection.end.offset - selection.start.offset, - attributes, - ); - } else { - for (var i = 0; i < textNodes.length; i++) { - final textNode = textNodes[i]; - var index = 0; - var length = textNode.toPlainText().length; - if (i == 0 && textNode == nodes.first) { - index = selection.start.offset; - length = textNode.toPlainText().length - selection.start.offset; - } else if (i == textNodes.length - 1 && textNode == nodes.last) { - length = selection.end.offset; - } - transaction.formatText( - textNode, - index, - length, - attributes, - ); - } - } - - editorState.apply(transaction); - - return true; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/editor_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/editor_service.dart deleted file mode 100644 index 86adfdecf1..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/editor_service.dart +++ /dev/null @@ -1,182 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/flutter/overlay.dart'; -import 'package:appflowy_editor/src/render/editor/editor_entry.dart'; -import 'package:appflowy_editor/src/render/image/image_node_builder.dart'; -import 'package:appflowy_editor/src/render/rich_text/bulleted_list_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/checkbox_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/heading_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/number_list_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/quoted_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/built_in_shortcut_events.dart'; -import 'package:flutter/material.dart' hide Overlay, OverlayEntry; - -NodeWidgetBuilders defaultBuilders = { - 'editor': EditorEntryWidgetBuilder(), - 'text': RichTextNodeWidgetBuilder(), - 'text/checkbox': CheckboxNodeWidgetBuilder(), - 'text/heading': HeadingTextNodeWidgetBuilder(), - 'text/bulleted-list': BulletedListTextNodeWidgetBuilder(), - 'text/number-list': NumberListTextNodeWidgetBuilder(), - 'text/quote': QuotedTextNodeWidgetBuilder(), - 'image': ImageNodeBuilder(), -}; - -class AppFlowyEditor extends StatefulWidget { - AppFlowyEditor({ - Key? key, - required this.editorState, - this.customBuilders = const {}, - this.shortcutEvents = const [], - this.selectionMenuItems = const [], - this.toolbarItems = const [], - this.editable = true, - this.autoFocus = false, - this.focusedSelection, - this.customActionMenuBuilder, - ThemeData? themeData, - }) : super(key: key) { - this.themeData = themeData ?? - ThemeData.light().copyWith(extensions: [ - ...lightEditorStyleExtension, - ...lightPluginStyleExtension, - ]); - } - - final EditorState editorState; - - /// Render plugins. - final NodeWidgetBuilders customBuilders; - - /// Keyboard event handlers. - final List shortcutEvents; - - final List selectionMenuItems; - - final List toolbarItems; - - late final ThemeData themeData; - - final bool editable; - - /// Set the value to true to focus the editor on the start of the document. - final bool autoFocus; - final Selection? focusedSelection; - - final Positioned Function(BuildContext context, List items)? - customActionMenuBuilder; - - @override - State createState() => _AppFlowyEditorState(); -} - -class _AppFlowyEditorState extends State { - Widget? services; - - EditorState get editorState => widget.editorState; - EditorStyle get editorStyle => - editorState.themeData.extension() ?? EditorStyle.light; - - @override - void initState() { - super.initState(); - - editorState.selectionMenuItems = widget.selectionMenuItems; - editorState.toolbarItems = widget.toolbarItems; - editorState.themeData = widget.themeData; - editorState.service.renderPluginService = _createRenderPlugin(); - editorState.editable = widget.editable; - - // auto focus - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - if (widget.editable && widget.autoFocus) { - editorState.service.selectionService.updateSelection( - widget.focusedSelection ?? - Selection.single(path: [0], startOffset: 0), - ); - } - }); - } - - @override - void didUpdateWidget(covariant AppFlowyEditor oldWidget) { - super.didUpdateWidget(oldWidget); - - if (editorState.service != oldWidget.editorState.service) { - editorState.selectionMenuItems = widget.selectionMenuItems; - editorState.toolbarItems = widget.toolbarItems; - editorState.service.renderPluginService = _createRenderPlugin(); - } - - editorState.themeData = widget.themeData; - editorState.editable = widget.editable; - services = null; - } - - @override - Widget build(BuildContext context) { - services ??= _buildServices(context); - return Overlay( - initialEntries: [ - OverlayEntry( - builder: (context) => services!, - ), - ], - ); - } - - Widget _buildServices(BuildContext context) { - return Theme( - data: widget.themeData, - child: AppFlowyScroll( - key: editorState.service.scrollServiceKey, - child: Container( - color: editorStyle.backgroundColor, - padding: editorStyle.padding!, - child: AppFlowySelection( - key: editorState.service.selectionServiceKey, - cursorColor: editorStyle.cursorColor!, - selectionColor: editorStyle.selectionColor!, - editorState: editorState, - editable: widget.editable, - child: AppFlowyInput( - key: editorState.service.inputServiceKey, - editorState: editorState, - editable: widget.editable, - child: AppFlowyKeyboard( - key: editorState.service.keyboardServiceKey, - editable: widget.editable, - shortcutEvents: [ - ...widget.shortcutEvents, - ...builtInShortcutEvents, - ], - editorState: editorState, - child: FlowyToolbar( - key: editorState.service.toolbarServiceKey, - editorState: editorState, - child: - editorState.service.renderPluginService.buildPluginWidget( - NodeWidgetContext( - context: context, - node: editorState.document.root, - editorState: editorState, - ), - ), - ), - ), - ), - ), - ), - ), - ); - } - - AppFlowyRenderPlugin _createRenderPlugin() => AppFlowyRenderPlugin( - editorState: editorState, - builders: { - ...defaultBuilders, - ...widget.customBuilders, - }, - customActionMenuBuilder: widget.customActionMenuBuilder, - ); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/input_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/input_service.dart deleted file mode 100644 index e3665650b4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/input_service.dart +++ /dev/null @@ -1,327 +0,0 @@ -import 'package:appflowy_editor/src/infra/log.dart'; -import 'package:appflowy_editor/src/core/transform/transaction.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/extensions/node_extensions.dart'; - -/// [AppFlowyInputService] is responsible for processing text input, -/// including text insertion, deletion and replacement. -/// -/// Usually, this service can be obtained by the following code. -/// ```dart -/// final inputService = editorState.service.inputService; -/// -/// /** update text editing value*/ -/// inputService?.attach(...); -/// -/// /** apply text editing deltas*/ -/// inputService?.apply(...); -/// ``` -/// -abstract class AppFlowyInputService { - /// Updates the [TextEditingValue] of the text currently being edited. - /// - /// Note that if there are IME-related requirements, - /// please config `composing` value within [TextEditingValue] - void attach(TextEditingValue textEditingValue); - - /// Applies insertion, deletion and replacement - /// to the text currently being edited. - /// - /// For more information, please check [TextEditingDelta]. - void apply(List deltas); - - /// Closes the editing state of the text currently being edited. - void close(); -} - -/// Processes text input -class AppFlowyInput extends StatefulWidget { - const AppFlowyInput({ - Key? key, - this.editable = true, - required this.editorState, - required this.child, - }) : super(key: key); - - final EditorState editorState; - final bool editable; - final Widget child; - - @override - State createState() => _AppFlowyInputState(); -} - -class _AppFlowyInputState extends State - implements AppFlowyInputService, DeltaTextInputClient { - TextInputConnection? _textInputConnection; - TextRange? _composingTextRange; - - EditorState get _editorState => widget.editorState; - - // Disable space shortcut on the Web platform. - final Map _shortcuts = kIsWeb - ? { - LogicalKeySet(LogicalKeyboardKey.space): - const DoNothingAndStopPropagationIntent(), - } - : {}; - - @override - void initState() { - super.initState(); - - if (widget.editable) { - _editorState.service.selectionService.currentSelection - .addListener(_onSelectionChange); - } - } - - @override - void dispose() { - if (widget.editable) { - close(); - _editorState.service.selectionService.currentSelection - .removeListener(_onSelectionChange); - } - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Shortcuts( - shortcuts: _shortcuts, - child: widget.child, - ); - } - - @override - void attach(TextEditingValue textEditingValue) { - if (_textInputConnection == null || - _textInputConnection!.attached == false) { - _textInputConnection = TextInput.attach( - this, - const TextInputConfiguration( - // TODO: customize - enableDeltaModel: true, - inputType: TextInputType.multiline, - textCapitalization: TextCapitalization.sentences, - ), - ); - } - - _textInputConnection! - ..setEditingState(textEditingValue) - ..show(); - } - - @override - void apply(List deltas) { - // TODO: implement the detail - for (final delta in deltas) { - _updateComposing(delta); - - if (delta is TextEditingDeltaInsertion) { - _applyInsert(delta); - } else if (delta is TextEditingDeltaDeletion) { - _applyDelete(delta); - } else if (delta is TextEditingDeltaReplacement) { - _applyReplacement(delta); - } else if (delta is TextEditingDeltaNonTextUpdate) {} - } - } - - void _updateComposing(TextEditingDelta delta) { - if (delta is! TextEditingDeltaNonTextUpdate) { - if (_composingTextRange != null && - delta.composing.end != -1 && - _composingTextRange!.start != -1) { - _composingTextRange = TextRange( - start: _composingTextRange!.start, - end: delta.composing.end, - ); - } else { - _composingTextRange = delta.composing; - } - } - } - - void _applyInsert(TextEditingDeltaInsertion delta) { - final selectionService = _editorState.service.selectionService; - final currentSelection = selectionService.currentSelection.value; - if (currentSelection == null) { - return; - } - if (currentSelection.isSingle) { - final textNode = selectionService.currentSelectedNodes.first as TextNode; - final transaction = _editorState.transaction; - transaction.insertText( - textNode, - delta.insertionOffset, - delta.textInserted, - ); - _editorState.apply(transaction); - } else { - // TODO: implement - } - } - - void _applyDelete(TextEditingDeltaDeletion delta) { - final selectionService = _editorState.service.selectionService; - final currentSelection = selectionService.currentSelection.value; - if (currentSelection == null) { - return; - } - if (currentSelection.isSingle) { - final textNode = selectionService.currentSelectedNodes.first as TextNode; - final length = delta.deletedRange.end - delta.deletedRange.start; - final transaction = _editorState.transaction; - transaction.deleteText(textNode, delta.deletedRange.start, length); - _editorState.apply(transaction); - } else { - // TODO: implement - } - } - - void _applyReplacement(TextEditingDeltaReplacement delta) { - final selectionService = _editorState.service.selectionService; - final currentSelection = selectionService.currentSelection.value; - if (currentSelection == null) { - return; - } - if (currentSelection.isSingle) { - final textNode = selectionService.currentSelectedNodes.first as TextNode; - final length = delta.replacedRange.end - delta.replacedRange.start; - final transaction = _editorState.transaction; - transaction.replaceText( - textNode, delta.replacedRange.start, length, delta.replacementText); - _editorState.apply(transaction); - } else { - // TODO: implement - } - } - - @override - void close() { - _textInputConnection?.close(); - _textInputConnection = null; - } - - @override - void connectionClosed() { - // TODO: implement connectionClosed - } - - @override - // TODO: implement currentAutofillScope - AutofillScope? get currentAutofillScope => throw UnimplementedError(); - - @override - // TODO: implement currentTextEditingValue - TextEditingValue? get currentTextEditingValue => throw UnimplementedError(); - - @override - void insertTextPlaceholder(Size size) { - // TODO: implement insertTextPlaceholder - } - - @override - void performAction(TextInputAction action) { - // TODO: implement performAction - } - - @override - void performPrivateCommand(String action, Map data) { - // TODO: implement performPrivateCommand - } - - @override - void removeTextPlaceholder() { - // TODO: implement removeTextPlaceholder - } - - @override - void showAutocorrectionPromptRect(int start, int end) { - // TODO: implement showAutocorrectionPromptRect - } - - @override - void showToolbar() { - // TODO: implement showToolbar - } - - @override - void updateEditingValue(TextEditingValue value) { - // TODO: implement updateEditingValue - } - - @override - void updateEditingValueWithDeltas(List textEditingDeltas) { - Log.input - .debug(textEditingDeltas.map((delta) => delta.toString()).toString()); - - apply(textEditingDeltas); - } - - @override - void updateFloatingCursor(RawFloatingCursorPoint point) { - // TODO: implement updateFloatingCursor - } - - void _onSelectionChange() { - final textNodes = _editorState.service.selectionService.currentSelectedNodes - .whereType(); - final selection = - _editorState.service.selectionService.currentSelection.value; - // FIXME: upward and selection update. - if (textNodes.isNotEmpty && selection != null) { - final text = textNodes.fold( - '', (sum, textNode) => '$sum${textNode.toPlainText()}\n'); - attach( - TextEditingValue( - text: text, - selection: TextSelection( - baseOffset: selection.start.offset, - extentOffset: selection.end.offset, - ), - composing: _composingTextRange ?? const TextRange.collapsed(-1), - ), - ); - if (textNodes.length == 1) { - _updateCaretPosition(textNodes.first, selection); - } - } else { - // https://github.com/flutter/flutter/issues/104944 - // Disable IME for the Web. - if (kIsWeb) { - close(); - } - } - } - - // TODO: support IME in linux / windows / ios / android - // Only support macOS now. - void _updateCaretPosition(TextNode textNode, Selection selection) { - if (!selection.isCollapsed) { - return; - } - final renderBox = textNode.renderBox; - final selectable = textNode.selectable; - if (renderBox != null && selectable != null) { - final size = renderBox.size; - final transform = renderBox.getTransformTo(null); - final rect = selectable.getCursorRectInPosition(selection.end); - if (rect != null) { - _textInputConnection - ?..setEditableSizeAndTransform(size, transform) - ..setCaretRect(rect); - } - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/arrow_keys_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/arrow_keys_handler.dart deleted file mode 100644 index 2d1953c68c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/arrow_keys_handler.dart +++ /dev/null @@ -1,481 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; - -ShortcutEventHandler cursorLeftSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - final end = selection.end.goLeft(editorState); - if (end == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorRightSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - final end = selection.end.goRight(editorState); - if (end == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorUpSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - final end = _goUp(editorState); - if (end == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorDownSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - final end = _goDown(editorState); - if (end == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorTop = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - if (nodes.isEmpty) { - return KeyEventResult.ignored; - } - final position = editorState.document.root.children - .whereType() - .first - .selectable - ?.start(); - if (position == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - Selection.collapsed(position), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorBottom = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - if (nodes.isEmpty) { - return KeyEventResult.ignored; - } - final position = editorState.document.root.children - .whereType() - .last - .selectable - ?.end(); - if (position == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - Selection.collapsed(position), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorBegin = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - if (nodes.isEmpty) { - return KeyEventResult.ignored; - } - final position = nodes.first.selectable?.start(); - if (position == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - Selection.collapsed(position), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorEnd = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - if (nodes.isEmpty) { - return KeyEventResult.ignored; - } - final position = nodes.first.selectable?.end(); - if (position == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - Selection.collapsed(position), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorTopSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - - var start = selection.start; - var end = selection.end; - final position = editorState.document.root.children - .whereType() - .first - .selectable - ?.start(); - if (position != null) { - end = position; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(start: start, end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorBottomSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - var start = selection.start; - var end = selection.end; - final position = editorState.document.root.children - .whereType() - .last - .selectable - ?.end(); - if (position != null) { - end = position; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(start: start, end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorBeginSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - - var start = selection.start; - var end = selection.end; - final position = nodes.last.selectable?.start(); - if (position != null) { - end = position; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(start: start, end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorEndSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - - var start = selection.start; - var end = selection.end; - final position = nodes.last.selectable?.end(); - if (position != null) { - end = position; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(start: start, end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorUp = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = - editorState.service.selectionService.currentSelection.value?.normalized; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - final upPosition = _goUp(editorState); - editorState.updateCursorSelection( - upPosition == null ? null : Selection.collapsed(upPosition), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorDown = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = - editorState.service.selectionService.currentSelection.value?.normalized; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - final downPosition = _goDown(editorState); - editorState.updateCursorSelection( - downPosition == null ? null : Selection.collapsed(downPosition), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorLeft = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = - editorState.service.selectionService.currentSelection.value?.normalized; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - if (selection.isCollapsed) { - final leftPosition = selection.start.goLeft(editorState); - if (leftPosition != null) { - editorState.service.selectionService.updateSelection( - Selection.collapsed(leftPosition), - ); - } - } else { - editorState.service.selectionService.updateSelection( - Selection.collapsed(selection.start), - ); - } - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorRight = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = - editorState.service.selectionService.currentSelection.value?.normalized; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - if (selection.isCollapsed) { - final rightPosition = selection.start.goRight(editorState); - if (rightPosition != null) { - editorState.service.selectionService.updateSelection( - Selection.collapsed(rightPosition), - ); - } - } else { - editorState.service.selectionService.updateSelection( - Selection.collapsed(selection.end), - ); - } - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorLeftWordSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - final end = - selection.end.goLeft(editorState, selectionRange: _SelectionRange.word); - if (end == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorRightWordSelect = (editorState, event) { - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (nodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - final end = - selection.end.goRight(editorState, selectionRange: _SelectionRange.word); - if (end == null) { - return KeyEventResult.ignored; - } - editorState.service.selectionService.updateSelection( - selection.copyWith(end: end), - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cursorLeftWordDelete = (editorState, event) { - final textNodes = editorState.service.selectionService.currentSelectedNodes - .whereType(); - final selection = editorState.service.selectionService.currentSelection.value; - - if (textNodes.isEmpty || selection == null) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - - final startOfWord = - selection.end.goLeft(editorState, selectionRange: _SelectionRange.word); - - if (startOfWord == null) { - return KeyEventResult.ignored; - } - - final transaction = editorState.transaction; - transaction.deleteText( - textNode, startOfWord.offset, selection.end.offset - startOfWord.offset); - - editorState.apply(transaction); - - return KeyEventResult.handled; -}; - -enum _SelectionRange { - character, - word, -} - -extension on Position { - Position? goLeft( - EditorState editorState, { - _SelectionRange selectionRange = _SelectionRange.character, - }) { - final node = editorState.document.nodeAtPath(path); - if (node == null) { - return null; - } - if (offset == 0) { - final previousEnd = node.previous?.selectable?.end(); - if (previousEnd != null) { - return previousEnd; - } - return null; - } - switch (selectionRange) { - case _SelectionRange.character: - if (node is TextNode) { - return Position( - path: path, - offset: node.delta.prevRunePosition(offset), - ); - } else { - return Position(path: path, offset: offset); - } - case _SelectionRange.word: - if (node is TextNode) { - final result = node.selectable?.getWordBoundaryInPosition( - Position( - path: path, - offset: node.delta.prevRunePosition(offset), - ), - ); - if (result != null) { - return result.start; - } - } else { - return Position(path: path, offset: offset); - } - } - return null; - } - - Position? goRight( - EditorState editorState, { - _SelectionRange selectionRange = _SelectionRange.character, - }) { - final node = editorState.document.nodeAtPath(path); - if (node == null) { - return null; - } - final end = node.selectable?.end(); - if (end != null && offset >= end.offset) { - final nextStart = node.next?.selectable?.start(); - if (nextStart != null) { - return nextStart; - } - return null; - } - switch (selectionRange) { - case _SelectionRange.character: - if (node is TextNode) { - return Position( - path: path, - offset: node.delta.nextRunePosition(offset), - ); - } else { - return Position(path: path, offset: offset); - } - case _SelectionRange.word: - if (node is TextNode) { - final result = node.selectable?.getWordBoundaryInPosition(this); - if (result != null) { - return result.end; - } - } else { - return Position(path: path, offset: offset); - } - } - return null; - } -} - -Position? _goUp(EditorState editorState) { - final selection = editorState.service.selectionService.currentSelection.value; - final rects = editorState.service.selectionService.selectionRects; - if (rects.isEmpty || selection == null) { - return null; - } - Offset offset; - if (selection.isBackward) { - final rect = rects.reduce( - (current, next) => current.bottom >= next.bottom ? current : next, - ); - offset = rect.topRight.translate(0, -rect.height); - } else { - final rect = rects.reduce( - (current, next) => current.top <= next.top ? current : next, - ); - offset = rect.topLeft.translate(0, -rect.height); - } - return editorState.service.selectionService.getPositionInOffset(offset); -} - -Position? _goDown(EditorState editorState) { - final selection = editorState.service.selectionService.currentSelection.value; - final rects = editorState.service.selectionService.selectionRects; - if (rects.isEmpty || selection == null) { - return null; - } - Offset offset; - if (selection.isBackward) { - final rect = rects.reduce( - (current, next) => current.bottom >= next.bottom ? current : next, - ); - offset = rect.bottomRight.translate(0, rect.height); - } else { - final rect = rects.reduce( - (current, next) => current.top <= next.top ? current : next, - ); - offset = rect.bottomLeft.translate(0, rect.height); - } - return editorState.service.selectionService.getPositionInOffset(offset); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart deleted file mode 100644 index 2a1f9db324..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart +++ /dev/null @@ -1,267 +0,0 @@ -import 'package:appflowy_editor/src/infra/infra.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/appflowy_editor.dart'; - -ShortcutEventHandler backspaceEventHandler = (editorState, event) { - var selection = editorState.service.selectionService.currentSelection.value; - if (selection == null) { - return KeyEventResult.ignored; - } - var nodes = editorState.service.selectionService.currentSelectedNodes; - nodes = selection.isBackward ? nodes : nodes.reversed.toList(growable: false); - selection = selection.isBackward ? selection : selection.reversed; - final textNodes = nodes.whereType().toList(); - final List nonTextNodes = nodes - .where((node) => node is! TextNode && node.selectable != null) - .toList(growable: false); - - final transaction = editorState.transaction; - List? cancelNumberListPath; - - if (nonTextNodes.isNotEmpty) { - transaction.deleteNodes(nonTextNodes); - } - - if (textNodes.length == 1) { - final textNode = textNodes.first; - final index = textNode.delta.prevRunePosition(selection.start.offset); - if (index < 0 && selection.isCollapsed) { - // 1. style - if (textNode.subtype != null) { - if (textNode.subtype == BuiltInAttributeKey.numberList) { - cancelNumberListPath = textNode.path; - } - transaction - ..updateNode(textNode, { - BuiltInAttributeKey.subtype: null, - textNode.subtype!: null, - }) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: 0, - ), - ); - } else { - // 2. non-style - // find previous text node. - return _backDeleteToPreviousTextNode( - editorState, - textNode, - transaction, - nonTextNodes, - selection, - ); - } - } else { - if (selection.isCollapsed) { - transaction.deleteText( - textNode, - index, - selection.start.offset - index, - ); - } else { - transaction.deleteText( - textNode, - selection.start.offset, - selection.end.offset - selection.start.offset, - ); - } - } - } else { - if (textNodes.isEmpty) { - if (nonTextNodes.isNotEmpty) { - transaction.afterSelection = Selection.collapsed(selection.start); - } - editorState.apply(transaction); - return KeyEventResult.handled; - } - final startPosition = selection.start; - final nodeAtStart = editorState.document.nodeAtPath(startPosition.path)!; - _deleteTextNodes(transaction, textNodes, selection); - editorState.apply(transaction); - - if (nodeAtStart is TextNode && - nodeAtStart.subtype == BuiltInAttributeKey.numberList) { - makeFollowingNodesIncremental( - editorState, - startPosition.path, - transaction.afterSelection!, - ); - } - return KeyEventResult.handled; - } - - if (transaction.operations.isNotEmpty) { - if (nonTextNodes.isNotEmpty) { - transaction.afterSelection = Selection.collapsed(selection.start); - } - editorState.apply(transaction); - } - - if (cancelNumberListPath != null) { - makeFollowingNodesIncremental( - editorState, - cancelNumberListPath, - Selection.collapsed(selection.start), - beginNum: 0, - ); - } - - return KeyEventResult.handled; -}; - -KeyEventResult _backDeleteToPreviousTextNode( - EditorState editorState, - TextNode textNode, - Transaction transaction, - List nonTextNodes, - Selection selection, -) { - if (textNode.next == null && - textNode.children.isEmpty && - textNode.parent?.parent != null && - textNode.parent is TextNode) { - transaction - ..deleteNode(textNode) - ..insertNode(textNode.parent!.path.next, textNode) - ..afterSelection = Selection.collapsed( - Position(path: textNode.parent!.path.next, offset: 0), - ); - editorState.apply(transaction); - return KeyEventResult.handled; - } - - bool prevIsNumberList = false; - final previousTextNode = Infra.forwardNearestTextNode(textNode); - if (previousTextNode != null) { - if (previousTextNode.subtype == BuiltInAttributeKey.numberList) { - prevIsNumberList = true; - } - - transaction.mergeText(previousTextNode, textNode); - if (textNode.children.isNotEmpty) { - transaction.insertNodes( - previousTextNode.path.next, - textNode.children.toList(growable: false), - ); - } - transaction.deleteNode(textNode); - transaction.afterSelection = Selection.collapsed( - Position( - path: previousTextNode.path, - offset: previousTextNode.toPlainText().length, - ), - ); - } - - if (transaction.operations.isNotEmpty) { - if (nonTextNodes.isNotEmpty) { - transaction.afterSelection = Selection.collapsed(selection.start); - } - editorState.apply(transaction); - } - - if (prevIsNumberList) { - makeFollowingNodesIncremental( - editorState, previousTextNode!.path, transaction.afterSelection!); - } - - return KeyEventResult.handled; -} - -ShortcutEventHandler deleteEventHandler = (editorState, event) { - var selection = editorState.service.selectionService.currentSelection.value; - if (selection == null) { - return KeyEventResult.ignored; - } - var nodes = editorState.service.selectionService.currentSelectedNodes; - nodes = selection.isBackward ? nodes : nodes.reversed.toList(growable: false); - selection = selection.isBackward ? selection : selection.reversed; - // make sure all nodes is [TextNode]. - final textNodes = nodes.whereType().toList(); - if (textNodes.length != nodes.length) { - return KeyEventResult.ignored; - } - - final transaction = editorState.transaction; - if (textNodes.length == 1) { - final textNode = textNodes.first; - // The cursor is at the end of the line, - // merge next line into this line. - if (selection.start.offset >= textNode.delta.length) { - return _mergeNextLineIntoThisLine( - editorState, - textNode, - transaction, - selection, - ); - } - final index = textNode.delta.nextRunePosition(selection.start.offset); - if (selection.isCollapsed) { - transaction.deleteText( - textNode, - selection.start.offset, - index - selection.start.offset, - ); - } else { - transaction.deleteText( - textNode, - selection.start.offset, - selection.end.offset - selection.start.offset, - ); - } - editorState.apply(transaction); - } else { - final startPosition = selection.start; - final nodeAtStart = editorState.document.nodeAtPath(startPosition.path)!; - _deleteTextNodes(transaction, textNodes, selection); - editorState.apply(transaction); - - if (nodeAtStart is TextNode && - nodeAtStart.subtype == BuiltInAttributeKey.numberList) { - makeFollowingNodesIncremental( - editorState, startPosition.path, transaction.afterSelection!); - } - } - - return KeyEventResult.handled; -}; - -KeyEventResult _mergeNextLineIntoThisLine(EditorState editorState, - TextNode textNode, Transaction transaction, Selection selection) { - final nextNode = textNode.next; - if (nextNode == null) { - return KeyEventResult.ignored; - } - if (nextNode is TextNode) { - transaction.mergeText(textNode, nextNode); - } - transaction.deleteNode(nextNode); - editorState.apply(transaction); - - if (textNode.subtype == BuiltInAttributeKey.numberList) { - makeFollowingNodesIncremental(editorState, textNode.path, selection); - } - - return KeyEventResult.handled; -} - -void _deleteTextNodes( - Transaction transaction, List textNodes, Selection selection) { - final first = textNodes.first; - final last = textNodes.last; - var content = textNodes.last.toPlainText(); - content = content.substring(selection.end.offset, content.length); - // Merge the first and the last text node content, - // and delete all the nodes except for the first. - transaction - ..deleteNodes(textNodes.sublist(1)) - ..mergeText( - first, - last, - firstOffset: selection.start.offset, - secondOffset: selection.end.offset, - ); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/checkbox_event_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/checkbox_event_handler.dart deleted file mode 100644 index 55b449c492..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/checkbox_event_handler.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; - -ShortcutEventHandler toggleCheckbox = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final checkboxTextNodes = nodes - .where( - (element) => - element is TextNode && - element.subtype == BuiltInAttributeKey.checkbox, - ) - .toList(growable: false); - - if (selection == null || checkboxTextNodes.isEmpty) { - return KeyEventResult.ignored; - } - - bool isAllCheckboxesChecked = checkboxTextNodes - .every((node) => node.attributes[BuiltInAttributeKey.checkbox] == true); - final transaction = editorState.transaction; - transaction.afterSelection = selection; - - if (isAllCheckboxesChecked) { - //if all the checkboxes are checked, then make all of the checkboxes unchecked - for (final node in checkboxTextNodes) { - transaction.updateNode(node, {BuiltInAttributeKey.checkbox: false}); - } - } else { - //If any one of the checkboxes is unchecked then make all checkboxes checked - for (final node in checkboxTextNodes) { - transaction.updateNode(node, {BuiltInAttributeKey.checkbox: true}); - } - } - - editorState.apply(transaction); - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart deleted file mode 100644 index 2a72400b1d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart +++ /dev/null @@ -1,376 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/infra/clipboard.dart'; -import 'package:appflowy_editor/src/infra/html_converter.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart'; -import 'package:flutter/material.dart'; - -int _textLengthOfNode(Node node) { - if (node is TextNode) { - return node.delta.length; - } - - return 0; -} - -Selection _computeSelectionAfterPasteMultipleNodes( - EditorState editorState, List nodes) { - final currentSelection = editorState.cursorSelection!; - final currentCursor = currentSelection.start; - final currentPath = [...currentCursor.path]; - currentPath[currentPath.length - 1] += nodes.length; - int lenOfLastNode = _textLengthOfNode(nodes.last); - return Selection.collapsed( - Position(path: currentPath, offset: lenOfLastNode)); -} - -void _handleCopy(EditorState editorState) async { - final selection = editorState.cursorSelection?.normalized; - if (selection == null || selection.isCollapsed) { - return; - } - if (selection.start.path.equals(selection.end.path)) { - final nodeAtPath = editorState.document.nodeAtPath(selection.end.path)!; - if (nodeAtPath.type == "text") { - final textNode = nodeAtPath as TextNode; - final htmlString = NodesToHTMLConverter( - nodes: [textNode], - startOffset: selection.start.offset, - endOffset: selection.end.offset) - .toHTMLString(); - final textString = textNode.toPlainText().substring( - selection.startIndex, - selection.endIndex, - ); - Log.keyboard.debug('copy html: $htmlString'); - AppFlowyClipboard.setData( - text: textString, - html: htmlString, - ); - } else { - Log.keyboard.debug('unimplemented: copy non-text'); - } - return; - } - - final beginNode = editorState.document.nodeAtPath(selection.start.path)!; - final endNode = editorState.document.nodeAtPath(selection.end.path)!; - - final nodes = NodeIterator( - document: editorState.document, - startNode: beginNode, - endNode: endNode, - ).toList(); - - final html = NodesToHTMLConverter( - nodes: nodes, - startOffset: selection.start.offset, - endOffset: selection.end.offset, - ).toHTMLString(); - var text = ''; - for (final node in nodes) { - if (node is TextNode) { - if (node.path == selection.start.path) { - text += node.toPlainText().substring(selection.start.offset); - } else if (node.path == selection.end.path) { - text += node.toPlainText().substring(0, selection.end.offset); - } else { - text += node.toPlainText(); - } - } - text += '\n'; - } - AppFlowyClipboard.setData( - text: text, - html: html, - ); -} - -void _pasteHTML(EditorState editorState, String html) { - final selection = editorState.cursorSelection?.normalized; - if (selection == null) { - return; - } - - assert(selection.isCollapsed); - - final path = [...selection.end.path]; - if (path.isEmpty) { - return; - } - - Log.keyboard.debug('paste html: $html'); - final nodes = HTMLToNodesConverter(html).toNodes(); - - if (nodes.isEmpty) { - return; - } else if (nodes.length == 1) { - final firstNode = nodes[0]; - final nodeAtPath = editorState.document.nodeAtPath(path)!; - final tb = editorState.transaction; - final startOffset = selection.start.offset; - if (nodeAtPath.type == "text" && firstNode.type == "text") { - final textNodeAtPath = nodeAtPath as TextNode; - final firstTextNode = firstNode as TextNode; - tb.updateText( - textNodeAtPath, (Delta()..retain(startOffset)) + firstTextNode.delta); - tb.updateNode(textNodeAtPath, firstTextNode.attributes); - tb.afterSelection = (Selection.collapsed(Position( - path: path, offset: startOffset + firstTextNode.delta.length))); - editorState.apply(tb); - return; - } - } - - _pasteMultipleLinesInText(editorState, path, selection.start.offset, nodes); -} - -void _pasteMultipleLinesInText( - EditorState editorState, List path, int offset, List nodes) { - final tb = editorState.transaction; - - final firstNode = nodes[0]; - final nodeAtPath = editorState.document.nodeAtPath(path)!; - - if (nodeAtPath.type == 'text' && firstNode.type == 'text') { - int? startNumber; - if (nodeAtPath.subtype == BuiltInAttributeKey.numberList) { - startNumber = nodeAtPath.attributes[BuiltInAttributeKey.number] as int; - } - - // split and merge - final textNodeAtPath = nodeAtPath as TextNode; - final firstTextNode = firstNode as TextNode; - final remain = textNodeAtPath.delta.slice(offset); - - tb.updateText( - textNodeAtPath, - (Delta() - ..retain(offset) - ..delete(remain.length)) + - firstTextNode.delta); - tb.updateNode(textNodeAtPath, firstTextNode.attributes); - - final tailNodes = nodes.sublist(1); - final originalPath = [...path]; - path[path.length - 1]++; - - final afterSelection = - _computeSelectionAfterPasteMultipleNodes(editorState, tailNodes); - - if (tailNodes.isNotEmpty) { - if (tailNodes.last.type == "text") { - final tailTextNode = tailNodes.last as TextNode; - tailTextNode.delta = tailTextNode.delta + remain; - } else if (remain.isNotEmpty) { - tailNodes.add(TextNode(delta: remain)); - } - } else { - tailNodes.add(TextNode(delta: remain)); - } - - tb.afterSelection = afterSelection; - tb.insertNodes(path, tailNodes); - editorState.apply(tb); - - if (startNumber != null) { - makeFollowingNodesIncremental(editorState, originalPath, afterSelection, - beginNum: startNumber); - } - return; - } - - final afterSelection = - _computeSelectionAfterPasteMultipleNodes(editorState, nodes); - - path[path.length - 1]++; - tb.afterSelection = afterSelection; - tb.insertNodes(path, nodes); - editorState.apply(tb); -} - -void _handlePaste(EditorState editorState) async { - final data = await AppFlowyClipboard.getData(); - - if (editorState.cursorSelection?.isCollapsed ?? false) { - _pastRichClipboard(editorState, data); - return; - } - - _deleteSelectedContent(editorState); - - WidgetsBinding.instance.addPostFrameCallback((_) { - _pastRichClipboard(editorState, data); - }); -} - -void _pastRichClipboard(EditorState editorState, AppFlowyClipboardData data) { - if (data.html != null) { - _pasteHTML(editorState, data.html!); - return; - } - if (data.text != null) { - _handlePastePlainText(editorState, data.text!); - return; - } -} - -void _pasteSingleLine( - EditorState editorState, Selection selection, String line) { - final node = editorState.document.nodeAtPath(selection.end.path)! as TextNode; - final beginOffset = selection.end.offset; - final transaction = editorState.transaction - ..updateText( - node, - Delta() - ..retain(beginOffset) - ..addAll(_lineContentToDelta(line))) - ..afterSelection = (Selection.collapsed( - Position(path: selection.end.path, offset: beginOffset + line.length))); - editorState.apply(transaction); -} - -/// parse url from the line text -/// reference: https://stackoverflow.com/questions/59444837/flutter-dart-regex-to-extract-urls-from-a-string -Delta _lineContentToDelta(String lineContent) { - final exp = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\#\w/\-?=%.]+'); - final Iterable matches = exp.allMatches(lineContent); - - final delta = Delta(); - - var lastUrlEndOffset = 0; - - for (final match in matches) { - if (lastUrlEndOffset < match.start) { - delta.insert(lineContent.substring(lastUrlEndOffset, match.start)); - } - final linkContent = lineContent.substring(match.start, match.end); - delta.insert(linkContent, attributes: {"href": linkContent}); - lastUrlEndOffset = match.end; - } - - if (lastUrlEndOffset < lineContent.length) { - delta.insert(lineContent.substring(lastUrlEndOffset, lineContent.length)); - } - - return delta; -} - -void _pasteMarkdown(EditorState editorState, String markdown) { - final selection = - editorState.service.selectionService.currentSelection.value?.normalized; - if (selection == null) { - return; - } - - final lines = markdown.split('\n'); - - if (lines.length == 1) { - _pasteSingleLine(editorState, selection, lines[0]); - return; - } - - var path = selection.end.path.next; - final node = editorState.document.nodeAtPath(selection.end.path); - if (node is TextNode && node.toPlainText().isEmpty) { - path = selection.end.path; - } - final document = markdownToDocument(markdown); - final transaction = editorState.transaction; - transaction.insertNodes(path, document.root.children); - editorState.apply(transaction); -} - -void _handlePastePlainText(EditorState editorState, String plainText) { - final selection = editorState.cursorSelection?.normalized; - if (selection == null) { - return; - } - - final lines = plainText - .split("\n") - .map((e) => e.replaceAll(RegExp(r'\r'), "")) - .toList(); - - if (lines.isEmpty) { - return; - } else if (lines.length == 1) { - // single line - _pasteSingleLine(editorState, selection, lines.first); - } else { - _pasteMarkdown(editorState, plainText); - } -} - -/// 1. copy the selected content -/// 2. delete selected content -void _handleCut(EditorState editorState) { - _handleCopy(editorState); - _deleteSelectedContent(editorState); -} - -void _deleteSelectedContent(EditorState editorState) { - final selection = editorState.cursorSelection?.normalized; - if (selection == null || selection.isCollapsed) { - return; - } - final beginNode = editorState.document.nodeAtPath(selection.start.path)!; - final endNode = editorState.document.nodeAtPath(selection.end.path)!; - if (selection.start.path.equals(selection.end.path) && - beginNode.type == "text") { - final textItem = beginNode as TextNode; - final tb = editorState.transaction; - final len = selection.end.offset - selection.start.offset; - tb.updateText( - textItem, - Delta() - ..retain(selection.start.offset) - ..delete(len)); - tb.afterSelection = Selection.collapsed(selection.start); - editorState.apply(tb); - return; - } - final traverser = NodeIterator( - document: editorState.document, - startNode: beginNode, - endNode: endNode, - ); - final tb = editorState.transaction; - while (traverser.moveNext()) { - final item = traverser.current; - if (item.type == "text" && beginNode == item) { - final textItem = item as TextNode; - final deleteLen = textItem.delta.length - selection.start.offset; - tb.updateText(textItem, () { - final delta = Delta() - ..retain(selection.start.offset) - ..delete(deleteLen); - - if (endNode is TextNode) { - final remain = endNode.delta.slice(selection.end.offset); - delta.addAll(remain); - } - - return delta; - }()); - } else { - tb.deleteNode(item); - } - } - tb.afterSelection = Selection.collapsed(selection.start); - editorState.apply(tb); -} - -ShortcutEventHandler copyEventHandler = (editorState, event) { - _handleCopy(editorState); - return KeyEventResult.handled; -}; - -ShortcutEventHandler pasteEventHandler = (editorState, event) { - _handlePaste(editorState); - return KeyEventResult.handled; -}; - -ShortcutEventHandler cutEventHandler = (editorState, event) { - _handleCut(editorState); - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart deleted file mode 100644 index 3c7acd16c4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart +++ /dev/null @@ -1,207 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; - -import './number_list_helper.dart'; - -/// Handle some cases where enter is pressed and shift is not pressed. -/// -/// 1. Multiple selection and the selected nodes are [TextNode] -/// 1.1 delete the nodes expect for the first and the last, -/// and delete the text in the first and the last node by case. -/// 2. Single selection and the selected node is [TextNode] -/// 2.1 split the node into two nodes with style -/// 2.2 or insert a empty text node before. -ShortcutEventHandler enterWithoutShiftInTextNodesHandler = - (editorState, event) { - var selection = editorState.service.selectionService.currentSelection.value; - var nodes = editorState.service.selectionService.currentSelectedNodes; - if (selection == null) { - return KeyEventResult.ignored; - } - if (selection.isForward) { - selection = selection.reversed; - nodes = nodes.reversed.toList(growable: false); - } - final textNodes = nodes.whereType().toList(growable: false); - - if (nodes.length != textNodes.length) { - return KeyEventResult.ignored; - } - - // Multiple selection - if (!selection.isSingle) { - final startNode = editorState.document.nodeAtPath(selection.start.path)!; - final length = textNodes.length; - final List subTextNodes = - length >= 3 ? textNodes.sublist(1, textNodes.length - 1) : []; - final afterSelection = Selection.collapsed( - Position(path: textNodes.first.path.next, offset: 0), - ); - final transaction = editorState.transaction - ..deleteText( - textNodes.first, - selection.start.offset, - textNodes.first.toPlainText().length, - ) - ..deleteNodes(subTextNodes) - ..deleteText( - textNodes.last, - 0, - selection.end.offset, - ) - ..afterSelection = afterSelection; - editorState.apply(transaction); - - if (startNode is TextNode && - startNode.subtype == BuiltInAttributeKey.numberList) { - makeFollowingNodesIncremental( - editorState, selection.start.path, afterSelection); - } - - return KeyEventResult.handled; - } - - // Single selection and the selected node is [TextNode] - if (textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - - // If selection is collapsed and position.start.offset == 0, - // insert a empty text node before. - if (selection.isCollapsed && selection.start.offset == 0) { - if (textNode.toPlainText().isEmpty && textNode.subtype != null) { - final afterSelection = Selection.collapsed( - Position(path: textNode.path, offset: 0), - ); - final transaction = editorState.transaction - ..updateNode(textNode, { - BuiltInAttributeKey.subtype: null, - textNode.subtype!: null, - }) - ..afterSelection = afterSelection; - editorState.apply(transaction); - - final nextNode = textNode.next; - if (nextNode is TextNode && - nextNode.subtype == BuiltInAttributeKey.numberList) { - makeFollowingNodesIncremental( - editorState, textNode.path, afterSelection, - beginNum: 0); - } - } else { - final subtype = textNode.subtype; - final afterSelection = Selection.collapsed( - Position(path: textNode.path.next, offset: 0), - ); - - if (subtype == BuiltInAttributeKey.numberList) { - final prevNumber = - textNode.attributes[BuiltInAttributeKey.number] as int; - final newNode = TextNode.empty(); - newNode.attributes[BuiltInAttributeKey.subtype] = - BuiltInAttributeKey.numberList; - newNode.attributes[BuiltInAttributeKey.number] = prevNumber; - final insertPath = textNode.path; - final transaction = editorState.transaction - ..insertNode( - insertPath, - newNode, - ) - ..afterSelection = afterSelection; - editorState.apply(transaction); - - makeFollowingNodesIncremental(editorState, insertPath, afterSelection, - beginNum: prevNumber); - } else { - bool needCopyAttributes = ![ - BuiltInAttributeKey.heading, - BuiltInAttributeKey.quote, - ].contains(subtype); - final transaction = editorState.transaction - ..insertNode( - textNode.path, - textNode.copyWith( - children: LinkedList(), - delta: Delta(), - attributes: needCopyAttributes ? null : {}, - ), - ) - ..afterSelection = afterSelection; - editorState.apply(transaction); - } - } - return KeyEventResult.handled; - } - - // Otherwise, - // split the node into two nodes with style - Attributes attributes = _attributesFromPreviousLine(textNode); - - final nextPath = textNode.path.next; - final afterSelection = Selection.collapsed( - Position(path: nextPath, offset: 0), - ); - - final transaction = editorState.transaction; - transaction.insertNode( - textNode.path.next, - textNode.copyWith( - attributes: attributes, - delta: textNode.delta.slice(selection.end.offset), - ), - ); - if (selection.end.offset != textNode.toPlainText().length) { - transaction.deleteText( - textNode, - selection.start.offset, - textNode.toPlainText().length - selection.start.offset, - ); - } - if (textNode.children.isNotEmpty) { - final children = textNode.children.toList(growable: false); - transaction.deleteNodes(children); - } - transaction.afterSelection = afterSelection; - editorState.apply(transaction); - - // If the new type of a text node is number list, - // the numbers of the following nodes should be incremental. - if (textNode.subtype == BuiltInAttributeKey.numberList) { - makeFollowingNodesIncremental(editorState, nextPath, afterSelection); - } - - return KeyEventResult.handled; -}; - -Attributes _attributesFromPreviousLine(TextNode textNode) { - final prevAttributes = textNode.attributes; - final subType = textNode.subtype; - if (subType == null || - subType == BuiltInAttributeKey.heading || - subType == BuiltInAttributeKey.quote) { - return {}; - } - - final copy = Attributes.from(prevAttributes); - if (subType == BuiltInAttributeKey.numberList) { - return _nextNumberAttributesFromPreviousLine(copy, textNode); - } - - if (subType == BuiltInAttributeKey.checkbox) { - copy[BuiltInAttributeKey.checkbox] = false; - return copy; - } - - return copy; -} - -Attributes _nextNumberAttributesFromPreviousLine( - Attributes copy, TextNode textNode) { - final prevNum = textNode.attributes[BuiltInAttributeKey.number] as int?; - copy[BuiltInAttributeKey.number] = prevNum == null ? 1 : prevNum + 1; - return copy; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/exit_editing_mode_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/exit_editing_mode_handler.dart deleted file mode 100644 index 2ffe0f3b96..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/exit_editing_mode_handler.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; -import 'package:flutter/material.dart'; - -ShortcutEventHandler exitEditingModeEventHandler = (editorState, event) { - editorState.service.selectionService.clearSelection(); - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart deleted file mode 100644 index 1535efdb94..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; -import 'package:flutter/material.dart'; - -import 'package:appflowy_editor/src/core/document/node.dart'; - -ShortcutEventHandler formatBoldEventHandler = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(growable: false); - if (selection == null || textNodes.isEmpty) { - return KeyEventResult.ignored; - } - formatBold(editorState); - return KeyEventResult.handled; -}; - -ShortcutEventHandler formatItalicEventHandler = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(growable: false); - if (selection == null || textNodes.isEmpty) { - return KeyEventResult.ignored; - } - formatItalic(editorState); - return KeyEventResult.handled; -}; - -ShortcutEventHandler formatUnderlineEventHandler = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(growable: false); - if (selection == null || textNodes.isEmpty) { - return KeyEventResult.ignored; - } - formatUnderline(editorState); - return KeyEventResult.handled; -}; - -ShortcutEventHandler formatStrikethroughEventHandler = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(growable: false); - if (selection == null || textNodes.isEmpty) { - return KeyEventResult.ignored; - } - formatStrikethrough(editorState); - return KeyEventResult.handled; -}; - -ShortcutEventHandler formatHighlightEventHandler = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(growable: false); - if (selection == null || textNodes.isEmpty) { - return KeyEventResult.ignored; - } - formatHighlight( - editorState, - editorState.editorStyle.highlightColorHex!, - ); - return KeyEventResult.handled; -}; - -ShortcutEventHandler formatLinkEventHandler = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(growable: false); - if (selection == null || textNodes.isEmpty) { - return KeyEventResult.ignored; - } - if (editorState.service.toolbarService - ?.triggerHandler('appflowy.toolbar.link') == - true) { - return KeyEventResult.handled; - } - return KeyEventResult.ignored; -}; - -ShortcutEventHandler formatEmbedCodeEventHandler = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final nodes = editorState.service.selectionService.currentSelectedNodes; - final textNodes = nodes.whereType().toList(growable: false); - if (selection == null || textNodes.isEmpty) { - return KeyEventResult.ignored; - } - formatEmbedCode(editorState); - return KeyEventResult.ignored; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart deleted file mode 100644 index ae2c354389..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart +++ /dev/null @@ -1,466 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; -import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; - -import 'package:flutter/material.dart'; - -bool _isCodeStyle(TextNode textNode, int index) { - return textNode.allSatisfyCodeInSelection(Selection.single( - path: textNode.path, startOffset: index, endOffset: index + 1)); -} - -// enter escape mode when start two backquote -bool _isEscapeBackquote(String text, List backquoteIndexes) { - if (backquoteIndexes.length >= 2) { - final firstBackquoteIndex = backquoteIndexes[0]; - final secondBackquoteIndex = backquoteIndexes[1]; - return firstBackquoteIndex == secondBackquoteIndex - 1; - } - return false; -} - -// find all the index of `, exclusion in code style. -List _findBackquoteIndexes(String text, TextNode textNode) { - final backquoteIndexes = []; - for (var i = 0; i < text.length; i++) { - if (text[i] == '`' && _isCodeStyle(textNode, i) == false) { - backquoteIndexes.add(i); - } - } - return backquoteIndexes; -} - -/// To denote a word or phrase as code, enclose it in backticks (`). -/// If the word or phrase you want to denote as code includes one or more -/// backticks, you can escape it by enclosing the word or phrase in double -/// backticks (``). -ShortcutEventHandler backquoteToCodeHandler = (editorState, event) { - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final textNodes = selectionService.currentSelectedNodes.whereType(); - - if (selection == null || !selection.isSingle || textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - final selectionText = textNode - .toPlainText() - .substring(selection.start.offset, selection.end.offset); - - // toggle code style when selected some text - if (selectionText.isNotEmpty) { - formatEmbedCode(editorState); - return KeyEventResult.handled; - } - - final text = textNode.toPlainText().substring(0, selection.end.offset); - final backquoteIndexes = _findBackquoteIndexes(text, textNode); - if (backquoteIndexes.isEmpty) { - return KeyEventResult.ignored; - } - - final endIndex = selection.end.offset; - - if (_isEscapeBackquote(text, backquoteIndexes)) { - final firstBackquoteIndex = backquoteIndexes[0]; - final secondBackquoteIndex = backquoteIndexes[1]; - final lastBackquoteIndex = backquoteIndexes[backquoteIndexes.length - 1]; - if (secondBackquoteIndex == lastBackquoteIndex || - secondBackquoteIndex == lastBackquoteIndex - 1 || - lastBackquoteIndex != endIndex - 1) { - // ``(`),```(`),``...`...(`) should ignored - return KeyEventResult.ignored; - } - - final transaction = editorState.transaction - ..deleteText(textNode, lastBackquoteIndex, 1) - ..deleteText(textNode, firstBackquoteIndex, 2) - ..formatText( - textNode, - firstBackquoteIndex, - endIndex - firstBackquoteIndex - 3, - { - BuiltInAttributeKey.code: true, - }, - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: endIndex - 3, - ), - ); - editorState.apply(transaction); - - return KeyEventResult.handled; - } - - // handle single backquote - final startIndex = backquoteIndexes[0]; - if (startIndex == endIndex - 1) { - return KeyEventResult.ignored; - } - - // delete the backquote. - // update the style of the text surround by ` ` to code. - // and update the cursor position. - final transaction = editorState.transaction - ..deleteText(textNode, startIndex, 1) - ..formatText( - textNode, - startIndex, - endIndex - startIndex - 1, - { - BuiltInAttributeKey.code: true, - }, - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: endIndex - 1, - ), - ); - editorState.apply(transaction); - - return KeyEventResult.handled; -}; - -// convert ~~abc~~ to strikethrough abc. -ShortcutEventHandler doubleTildeToStrikethrough = (editorState, event) { - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final textNodes = selectionService.currentSelectedNodes.whereType(); - if (selection == null || !selection.isSingle || textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - final text = textNode.toPlainText().substring(0, selection.end.offset); - - // make sure the last two characters are ~~. - if (text.length < 2 || text[selection.end.offset - 1] != '~') { - return KeyEventResult.ignored; - } - - // find all the index of `~`. - final tildeIndexes = []; - for (var i = 0; i < text.length; i++) { - if (text[i] == '~') { - tildeIndexes.add(i); - } - } - - if (tildeIndexes.length < 3) { - return KeyEventResult.ignored; - } - - // make sure the second to last and third to last tildes are connected. - final thirdToLastTildeIndex = tildeIndexes[tildeIndexes.length - 3]; - final secondToLastTildeIndex = tildeIndexes[tildeIndexes.length - 2]; - final lastTildeIndex = tildeIndexes[tildeIndexes.length - 1]; - if (secondToLastTildeIndex != thirdToLastTildeIndex + 1 || - lastTildeIndex == secondToLastTildeIndex + 1) { - return KeyEventResult.ignored; - } - - // delete the last three tildes. - // update the style of the text surround by `~~ ~~` to strikethrough. - // and update the cursor position. - final transaction = editorState.transaction - ..deleteText(textNode, lastTildeIndex, 1) - ..deleteText(textNode, thirdToLastTildeIndex, 2) - ..formatText( - textNode, - thirdToLastTildeIndex, - selection.end.offset - thirdToLastTildeIndex - 2, - { - BuiltInAttributeKey.strikethrough: true, - }, - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: selection.end.offset - 3, - ), - ); - editorState.apply(transaction); - - return KeyEventResult.handled; -}; - -ShortcutEventHandler markdownLinkOrImageHandler = (editorState, event) { - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final textNodes = selectionService.currentSelectedNodes.whereType(); - if (selection == null || !selection.isSingle || textNodes.length != 1) { - return KeyEventResult.ignored; - } - - // Find all of the indexes of the relevant characters - final textNode = textNodes.first; - final text = textNode.toPlainText(); - final firstExclamation = text.indexOf('!'); - final firstOpeningBracket = text.indexOf('['); - final firstClosingBracket = text.indexOf(']'); - - // Use RegEx to determine whether it's an image or a link - // Difference between image and link syntax is that image - // has an exclamation point at the beginning. - // Note: The RegEx enforces that the URL has http or https - final imgRegEx = - RegExp(r'\!\[([\w\s\d]+)\]\(((?:\/|https?:\/\/)[\w\d-./?=#%&]+)$'); - final lnkRegEx = - RegExp(r'\[([\w\s\d]+)\]\(((?:\/|https?:\/\/)[\w\d-./?=#%&]+)$'); - - if (imgRegEx.firstMatch(text) != null) { - // Extract the alt text and the URL of the image - final match = lnkRegEx.firstMatch(text); - final imgUrl = match?.group(2); - - // Delete the text and replace it with the image pointed to by the URL - final transaction = editorState.transaction - ..deleteText(textNode, firstExclamation, text.length) - ..insertNode( - textNode.path, - Node.fromJson({ - 'type': 'image', - 'attributes': { - 'image_src': imgUrl, - 'align': 'center', - } - })); - editorState.apply(transaction); - } else if (lnkRegEx.firstMatch(text) != null) { - // Extract the text and the URL of the link - final match = lnkRegEx.firstMatch(text); - final linkText = match?.group(1); - final linkUrl = match?.group(2); - - // Delete the initial opening bracket, - // update the href attribute of the text surrounded by [ ] to the url, - // delete everything after the text, - // and update the cursor position. - final transaction = editorState.transaction - ..deleteText(textNode, firstOpeningBracket, 1) - ..formatText( - textNode, - firstOpeningBracket, - firstClosingBracket - firstOpeningBracket - 1, - { - BuiltInAttributeKey.href: linkUrl, - }, - ) - ..deleteText(textNode, firstClosingBracket - 1, - selection.end.offset - firstClosingBracket) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: firstOpeningBracket + linkText!.length, - ), - ); - editorState.apply(transaction); - } else { - return KeyEventResult.ignored; - } - return KeyEventResult.handled; -}; - -ShortcutEventHandler underscoreToItalicHandler = (editorState, event) { - // Obtain the selection and selected nodes of the current document through the 'selectionService' - // to determine whether the selection is collapsed and whether the selected node is a text node. - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final textNodes = selectionService.currentSelectedNodes.whereType(); - if (selection == null || !selection.isSingle || textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - final text = textNode.toPlainText(); - // Determine if an 'underscore' already exists in the text node and only once. - final firstUnderscore = text.indexOf('_'); - final lastUnderscore = text.lastIndexOf('_'); - if (firstUnderscore == -1 || - firstUnderscore != lastUnderscore || - firstUnderscore == selection.start.offset - 1) { - return KeyEventResult.ignored; - } - - // Delete the previous 'underscore', - // update the style of the text surrounded by the two underscores to 'italic', - // and update the cursor position. - final transaction = editorState.transaction - ..deleteText(textNode, firstUnderscore, 1) - ..formatText( - textNode, - firstUnderscore, - selection.end.offset - firstUnderscore - 1, - { - BuiltInAttributeKey.italic: true, - }, - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: selection.end.offset - 1, - ), - ); - editorState.apply(transaction); - - return KeyEventResult.handled; -}; - -//Same functionality implemented via Asterisk - for italics -ShortcutEventHandler asteriskToItalicHandler = (editorState, event) { - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final textNodes = selectionService.currentSelectedNodes.whereType(); - if (selection == null || !selection.isSingle || textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - final text = textNode.toPlainText(); - // Determine if an 'asterisk' already exists in the text node and only once. - final firstAsterisk = text.indexOf('*'); - final lastAsterisk = text.lastIndexOf('*'); - if (firstAsterisk == -1 || - firstAsterisk != lastAsterisk || - firstAsterisk == selection.start.offset - 1) { - return KeyEventResult.ignored; - } - - final transaction = editorState.transaction - ..deleteText(textNode, firstAsterisk, 1) - ..formatText( - textNode, - firstAsterisk, - selection.end.offset - firstAsterisk - 1, - { - BuiltInAttributeKey.italic: true, - }, - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: selection.end.offset - 1, - ), - ); - editorState.apply(transaction); - - return KeyEventResult.handled; -}; - -ShortcutEventHandler doubleAsteriskToBoldHandler = (editorState, event) { - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final textNodes = selectionService.currentSelectedNodes.whereType(); - - if (selection == null || !selection.isSingle || textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - final text = textNode.toPlainText(); - -// make sure the last two characters are '**' - if (text.length < 2 || text[selection.end.offset - 1] != '*') { - return KeyEventResult.ignored; - } - -// find all the index of '*' - final asteriskIndexList = []; - for (var i = 0; i < text.length; i++) { - if (text[i] == '*') { - asteriskIndexList.add(i); - } - } - - if (asteriskIndexList.length < 3) return KeyEventResult.ignored; - -// make sure the second to last and third to last asterisk are connected - final thirdToLastAsteriskIndex = - asteriskIndexList[asteriskIndexList.length - 3]; - final secondToLastAsteriskIndex = - asteriskIndexList[asteriskIndexList.length - 2]; - final lastAsteriskIndex = asteriskIndexList[asteriskIndexList.length - 1]; - if (secondToLastAsteriskIndex != thirdToLastAsteriskIndex + 1 || - lastAsteriskIndex == secondToLastAsteriskIndex + 1) { - return KeyEventResult.ignored; - } - -//delete the last three asterisks -//update the style of the text surround by '** **' to bold -//update the cursor position - final transaction = editorState.transaction - ..deleteText(textNode, lastAsteriskIndex, 1) - ..deleteText(textNode, thirdToLastAsteriskIndex, 2) - ..formatText(textNode, thirdToLastAsteriskIndex, - selection.end.offset - thirdToLastAsteriskIndex - 2, { - BuiltInAttributeKey.bold: true, - }) - ..afterSelection = Selection.collapsed( - Position(path: textNode.path, offset: selection.end.offset - 3)); - - editorState.apply(transaction); - - return KeyEventResult.handled; -}; - -//Implement in the same way as doubleAsteriskToBoldHandler -ShortcutEventHandler doubleUnderscoreToBoldHandler = (editorState, event) { - final selectionService = editorState.service.selectionService; - final selection = selectionService.currentSelection.value; - final textNodes = selectionService.currentSelectedNodes.whereType(); - - if (selection == null || !selection.isSingle || textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - final text = textNode.toPlainText(); - -// make sure the last two characters are '__' - if (text.length < 2 || text[selection.end.offset - 1] != '_') { - return KeyEventResult.ignored; - } - -// find all the index of '_' - final underscoreIndexList = []; - for (var i = 0; i < text.length; i++) { - if (text[i] == '_') { - underscoreIndexList.add(i); - } - } - - if (underscoreIndexList.length < 3) return KeyEventResult.ignored; - -// make sure the second to last and third to last underscore are connected - final thirdToLastUnderscoreIndex = - underscoreIndexList[underscoreIndexList.length - 3]; - final secondToLastUnderscoreIndex = - underscoreIndexList[underscoreIndexList.length - 2]; - final lastUnderscoreIndex = - underscoreIndexList[underscoreIndexList.length - 1]; - if (secondToLastUnderscoreIndex != thirdToLastUnderscoreIndex + 1 || - lastUnderscoreIndex == secondToLastUnderscoreIndex + 1) { - return KeyEventResult.ignored; - } - -//delete the last three underscores -//update the style of the text surround by '__ __' to bold -//update the cursor position - final transaction = editorState.transaction - ..deleteText(textNode, lastUnderscoreIndex, 1) - ..deleteText(textNode, thirdToLastUnderscoreIndex, 2) - ..formatText(textNode, thirdToLastUnderscoreIndex, - selection.end.offset - thirdToLastUnderscoreIndex - 2, { - BuiltInAttributeKey.bold: true, - }) - ..afterSelection = Selection.collapsed( - Position(path: textNode.path, offset: selection.end.offset - 3)); - - editorState.apply(transaction); - - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart deleted file mode 100644 index d4d14bc204..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; -import 'package:appflowy_editor/src/core/document/attributes.dart'; - -void makeFollowingNodesIncremental( - EditorState editorState, List insertPath, Selection afterSelection, - {int? beginNum}) { - final insertNode = editorState.document.nodeAtPath(insertPath); - if (insertNode == null) { - return; - } - beginNum ??= insertNode.attributes[BuiltInAttributeKey.number] as int; - - int numPtr = beginNum + 1; - var ptr = insertNode.next; - - final transaction = editorState.transaction; - - while (ptr != null) { - if (ptr.subtype != BuiltInAttributeKey.numberList) { - break; - } - final currentNum = ptr.attributes[BuiltInAttributeKey.number] as int; - if (currentNum != numPtr) { - Attributes updateAttributes = {}; - updateAttributes[BuiltInAttributeKey.number] = numPtr; - transaction.updateNode(ptr, updateAttributes); - } - - ptr = ptr.next; - numPtr++; - } - - transaction.afterSelection = afterSelection; - editorState.apply(transaction); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/page_up_down_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/page_up_down_handler.dart deleted file mode 100644 index f10559ff25..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/page_up_down_handler.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; - -ShortcutEventHandler pageUpHandler = (editorState, _) { - final scrollHeight = editorState.service.scrollService?.onePageHeight; - final scrollService = editorState.service.scrollService; - if (scrollHeight != null && scrollService != null) { - scrollService.scrollTo(scrollService.dy - scrollHeight); - } - return KeyEventResult.handled; -}; - -ShortcutEventHandler pageDownHandler = (editorState, _) { - final scrollHeight = editorState.service.scrollService?.onePageHeight; - final scrollService = editorState.service.scrollService; - if (scrollHeight != null && scrollService != null) { - scrollService.scrollTo(scrollService.dy + scrollHeight); - } - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/redo_undo_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/redo_undo_handler.dart deleted file mode 100644 index e20d6dc43d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/redo_undo_handler.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; - -ShortcutEventHandler redoEventHandler = (editorState, event) { - editorState.undoManager.redo(); - return KeyEventResult.handled; -}; - -ShortcutEventHandler undoEventHandler = (editorState, event) { - editorState.undoManager.undo(); - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/select_all_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/select_all_handler.dart deleted file mode 100644 index bc130fd6bf..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/select_all_handler.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; -import 'package:flutter/material.dart'; - -ShortcutEventHandler selectAllHandler = (editorState, event) { - if (editorState.document.root.children.isEmpty) { - return KeyEventResult.handled; - } - final firstNode = editorState.document.root.children.firstWhere( - (element) => element is TextNode, - ); - final lastNode = editorState.document.root.children.last; - var offset = 0; - if (lastNode is TextNode) { - offset = lastNode.delta.length; - } - editorState.updateCursorSelection( - Selection( - start: Position(path: firstNode.path, offset: 0), - end: Position(path: lastNode.path, offset: offset), - ), - ); - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/slash_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/slash_handler.dart deleted file mode 100644 index 9fa7eacce6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/slash_handler.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/transform/transaction.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; -import 'package:appflowy_editor/src/extensions/node_extensions.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; -import 'package:flutter/material.dart'; - -SelectionMenuService? _selectionMenuService; -ShortcutEventHandler slashShortcutHandler = (editorState, event) { - final textNodes = editorState.service.selectionService.currentSelectedNodes - .whereType(); - if (textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final selection = editorState.service.selectionService.currentSelection.value; - final textNode = textNodes.first; - final context = textNode.context; - final selectable = textNode.selectable; - if (selection == null || context == null || selectable == null) { - return KeyEventResult.ignored; - } - final transaction = editorState.transaction - ..replaceText( - textNode, - selection.start.offset, - selection.end.offset - selection.start.offset, - '/', - ); - editorState.apply(transaction); - - WidgetsBinding.instance.addPostFrameCallback((_) { - _selectionMenuService = - SelectionMenu(context: context, editorState: editorState); - _selectionMenuService?.show(); - }); - - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/space_on_web_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/space_on_web_handler.dart deleted file mode 100644 index a69b2f4447..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/space_on_web_handler.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -ShortcutEventHandler spaceOnWebHandler = (editorState, event) { - final selection = editorState.service.selectionService.currentSelection.value; - final textNodes = editorState.service.selectionService.currentSelectedNodes - .whereType() - .toList(growable: false); - if (selection == null || - !selection.isCollapsed || - !kIsWeb || - textNodes.length != 1) { - return KeyEventResult.ignored; - } - - editorState.insertText( - selection.startIndex, - ' ', - textNode: textNodes.first, - ); - - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/tab_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/tab_handler.dart deleted file mode 100644 index 1b079e03c5..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/tab_handler.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; - -ShortcutEventHandler tabHandler = (editorState, event) { - // Only Supports BulletedList For Now. - - final selection = editorState.service.selectionService.currentSelection.value; - final textNodes = editorState.service.selectionService.currentSelectedNodes - .whereType(); - if (textNodes.length != 1 || selection == null || !selection.isSingle) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - final previous = textNode.previous; - - if (textNode.subtype != BuiltInAttributeKey.bulletedList && - textNode.subtype != BuiltInAttributeKey.checkbox) { - final transaction = editorState.transaction - ..insertText(textNode, selection.end.offset, ' ' * 4); - editorState.apply(transaction); - return KeyEventResult.handled; - } - - if (previous == null || - (previous.subtype != BuiltInAttributeKey.bulletedList && - previous.subtype != BuiltInAttributeKey.checkbox)) { - return KeyEventResult.ignored; - } - - final path = previous.path + [previous.children.length]; - final afterSelection = Selection( - start: selection.start.copyWith(path: path), - end: selection.end.copyWith(path: path), - ); - final transaction = editorState.transaction - ..deleteNode(textNode) - ..insertNode(path, textNode) - ..afterSelection = afterSelection; - editorState.apply(transaction); - - return KeyEventResult.handled; -}; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart deleted file mode 100644 index 1b3f1abc81..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart +++ /dev/null @@ -1,223 +0,0 @@ -import 'package:appflowy_editor/src/core/transform/transaction.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart'; -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import './number_list_helper.dart'; -import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; - -@visibleForTesting -List get checkboxListSymbols => _checkboxListSymbols; -@visibleForTesting -List get unCheckboxListSymbols => _unCheckboxListSymbols; -@visibleForTesting -List get bulletedListSymbols => _bulletedListSymbols; - -const _bulletedListSymbols = ['*', '-']; -const _checkboxListSymbols = ['[x]', '-[x]']; -const _unCheckboxListSymbols = ['[]', '-[]']; - -const _quoteSymbols = ['>']; - -final _numberRegex = RegExp(r'^(\d+)\.'); - -ShortcutEventHandler whiteSpaceHandler = (editorState, event) { - /// Process markdown input style. - /// - /// like, #, *, -, 1., -[], - - final selection = editorState.service.selectionService.currentSelection.value; - if (selection == null || !selection.isCollapsed) { - return KeyEventResult.ignored; - } - - final textNodes = editorState.service.selectionService.currentSelectedNodes - .whereType(); - if (textNodes.length != 1) { - return KeyEventResult.ignored; - } - - final textNode = textNodes.first; - final text = textNode.toPlainText().substring(0, selection.end.offset); - - final numberMatch = _numberRegex.firstMatch(text); - - if ((_checkboxListSymbols + _unCheckboxListSymbols).contains(text)) { - return _toCheckboxList(editorState, textNode); - } else if (_bulletedListSymbols.contains(text)) { - return _toBulletedList(editorState, textNode); - } else if (_countOfSign(text, selection) != 0) { - return _toHeadingStyle(editorState, textNode, selection); - } else if (_quoteSymbols.contains(text)) { - return _toQuoteStyle(editorState, textNode); - } else if (numberMatch != null) { - final matchText = numberMatch.group(0); - final numText = numberMatch.group(1); - if (matchText != null && - numText != null && - matchText.length == selection.startIndex) { - return _toNumberList(editorState, textNode, matchText, numText); - } - } - - return KeyEventResult.ignored; -}; - -KeyEventResult _toNumberList(EditorState editorState, TextNode textNode, - String matchText, String numText) { - if (textNode.subtype == BuiltInAttributeKey.bulletedList) { - return KeyEventResult.ignored; - } - - final numValue = int.tryParse(numText); - if (numValue == null) { - return KeyEventResult.ignored; - } - - // The user types number + . + space, he wants to turn - // this line into number list, but we should check if previous line - // is number list. - // - // Check whether the number input by the user is the successor of the previous - // line. If it's not, ignore it. - final prevNode = textNode.previous; - if (prevNode != null && - prevNode is TextNode && - prevNode.attributes[BuiltInAttributeKey.subtype] == - BuiltInAttributeKey.numberList) { - final prevNumber = prevNode.attributes[BuiltInAttributeKey.number] as int; - if (numValue != prevNumber + 1) { - return KeyEventResult.ignored; - } - } - - final afterSelection = Selection.collapsed(Position( - path: textNode.path, - offset: 0, - )); - - final insertPath = textNode.path; - final transaction = editorState.transaction - ..deleteText(textNode, 0, matchText.length) - ..updateNode(textNode, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.numberList, - BuiltInAttributeKey.number: numValue - }) - ..afterSelection = afterSelection; - editorState.apply(transaction); - - makeFollowingNodesIncremental(editorState, insertPath, afterSelection); - - return KeyEventResult.handled; -} - -KeyEventResult _toBulletedList(EditorState editorState, TextNode textNode) { - if (textNode.subtype == BuiltInAttributeKey.bulletedList) { - return KeyEventResult.ignored; - } - final transaction = editorState.transaction - ..deleteText(textNode, 0, 1) - ..updateNode(textNode, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: 0, - ), - ); - editorState.apply(transaction); - return KeyEventResult.handled; -} - -KeyEventResult _toCheckboxList(EditorState editorState, TextNode textNode) { - if (textNode.subtype == BuiltInAttributeKey.checkbox) { - return KeyEventResult.ignored; - } - final String symbol; - bool check = false; - final symbols = List.from(_checkboxListSymbols) - ..retainWhere(textNode.toPlainText().startsWith); - if (symbols.isNotEmpty) { - symbol = symbols.first; - check = true; - } else { - symbol = (List.from(_unCheckboxListSymbols) - ..retainWhere(textNode.toPlainText().startsWith)) - .first; - check = false; - } - - final transaction = editorState.transaction - ..deleteText(textNode, 0, symbol.length) - ..updateNode(textNode, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: check, - }) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: 0, - ), - ); - editorState.apply(transaction); - return KeyEventResult.handled; -} - -KeyEventResult _toHeadingStyle( - EditorState editorState, TextNode textNode, Selection selection) { - final x = _countOfSign( - textNode.toPlainText(), - selection, - ); - final hX = 'h$x'; - if (textNode.attributes.heading == hX) { - return KeyEventResult.ignored; - } - final transaction = editorState.transaction - ..deleteText(textNode, 0, x) - ..updateNode(textNode, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: hX, - }) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: 0, - ), - ); - editorState.apply(transaction); - return KeyEventResult.handled; -} - -int _countOfSign(String text, Selection selection) { - for (var i = 6; i >= 0; i--) { - final heading = text.substring(0, selection.end.offset); - if (heading.contains('#' * i) && heading.length == i) { - return i; - } - } - return 0; -} - -KeyEventResult _toQuoteStyle(EditorState editorState, TextNode textNode) { - if (textNode.subtype == BuiltInAttributeKey.quote) { - return KeyEventResult.ignored; - } - final transaction = editorState.transaction - ..deleteText(textNode, 0, 1) - ..updateNode(textNode, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: 0, - ), - ); - editorState.apply(transaction); - return KeyEventResult.handled; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart deleted file mode 100644 index bcd0f769fb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/keyboard_service.dart +++ /dev/null @@ -1,164 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; - -import 'package:flutter/material.dart'; - -/// [AppFlowyKeyboardService] is responsible for processing shortcut keys, -/// like command, shift, control keys. -/// -/// Usually, this service can be obtained by the following code. -/// ```dart -/// final keyboardService = editorState.service.keyboardService; -/// -/// /** Simulates shortcut key input*/ -/// keyboardService?.onKey(...); -/// -/// /** Enables or disables this service */ -/// keyboardService?.enable(); -/// keyboardService?.disable(); -/// ``` -/// -abstract class AppFlowyKeyboardService { - /// Processes shortcut key input. - KeyEventResult onKey(RawKeyEvent event); - - /// Gets the shortcut events - List get shortcutEvents; - - /// Enables shortcuts service. - void enable(); - - /// Disables shortcuts service. - /// - /// In some cases, if your custom component needs to monitor - /// keyboard events separately, - /// you can disable the keyboard service of flowy_editor. - /// But you need to call the `enable` function to restore after exiting - /// your custom component, otherwise the keyboard service will fails. - void disable({ - bool showCursor = false, - UnfocusDisposition disposition = UnfocusDisposition.scope, - }); -} - -/// Process keyboard events -class AppFlowyKeyboard extends StatefulWidget { - const AppFlowyKeyboard({ - Key? key, - this.editable = true, - required this.shortcutEvents, - required this.editorState, - required this.child, - }) : super(key: key); - - final EditorState editorState; - final Widget child; - final List shortcutEvents; - final bool editable; - - @override - State createState() => _AppFlowyKeyboardState(); -} - -class _AppFlowyKeyboardState extends State - implements AppFlowyKeyboardService { - final FocusNode _focusNode = FocusNode(debugLabel: 'flowy_keyboard_service'); - - bool isFocus = true; - bool showCursor = false; - - @override - List get shortcutEvents => widget.shortcutEvents; - - @override - Widget build(BuildContext context) { - return Focus( - focusNode: _focusNode, - onKey: _onKey, - onFocusChange: _onFocusChange, - child: widget.child, - ); - } - - @override - void initState() { - super.initState(); - - enable(); - } - - @override - void dispose() { - _focusNode.dispose(); - - super.dispose(); - } - - @override - void enable() { - if (widget.editable) { - isFocus = true; - _focusNode.requestFocus(); - } else { - disable(); - } - } - - @override - void disable({ - bool showCursor = false, - UnfocusDisposition disposition = UnfocusDisposition.scope, - }) { - isFocus = false; - this.showCursor = showCursor; - _focusNode.unfocus(disposition: disposition); - _onFocusChange(false); - } - - @override - KeyEventResult onKey(RawKeyEvent event) { - if (!isFocus) { - return KeyEventResult.ignored; - } - - if (event is! RawKeyDownEvent) { - return KeyEventResult.ignored; - } - - // TODO: use cache to optimize the searching time. - for (final shortcutEvent in widget.shortcutEvents) { - if (shortcutEvent.canRespondToRawKeyEvent(event)) { - final result = shortcutEvent.handler(widget.editorState, event); - if (result == KeyEventResult.handled) { - return KeyEventResult.handled; - } else if (result == KeyEventResult.skipRemainingHandlers) { - return KeyEventResult.skipRemainingHandlers; - } - continue; - } - } - - return KeyEventResult.ignored; - } - - void _onFocusChange(bool value) { - Log.keyboard.debug('on keyboard event focus change $value'); - isFocus = value; - if (!value && !showCursor) { - widget.editorState.service.selectionService.clearCursor(); - } else { - showCursor = false; - } - } - - KeyEventResult _onKey(FocusNode node, RawKeyEvent event) { - return onKey(event); - } -} - -extension on ShortcutEvent { - bool canRespondToRawKeyEvent(RawKeyEvent event) { - return ((character?.isNotEmpty ?? false) && character == event.character) || - keybindings.containsKeyEvent(event); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/render_plugin_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/render_plugin_service.dart deleted file mode 100644 index 24adece17a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/render_plugin_service.dart +++ /dev/null @@ -1,180 +0,0 @@ -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/infra/log.dart'; -import 'package:appflowy_editor/src/render/action_menu/action_menu.dart'; -import 'package:appflowy_editor/src/render/action_menu/action_menu_item.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -typedef NodeValidator = bool Function(T node); - -abstract class NodeWidgetBuilder { - NodeValidator get nodeValidator; - - Widget build(NodeWidgetContext context); -} - -typedef NodeWidgetBuilders = Map; - -abstract class AppFlowyRenderPluginService { - /// Register render plugin with specified [name]. - /// - /// [name] should be [Node].type - /// or `[Node].type + '/' + [Node].attributes['subtype']`. - /// - /// e.g. 'text', 'text/checkbox', or 'text/heading' - /// - /// [name] could be empty. - void register(String name, NodeWidgetBuilder builder); - void registerAll(Map builders); - - /// UnRegister plugin with specified [name]. - void unRegister(String name); - - /// Returns a [NodeWidgetBuilder], if one has been registered for [name] - NodeWidgetBuilder? getBuilder(String name); - - Widget buildPluginWidget(NodeWidgetContext context); -} - -class NodeWidgetContext { - final BuildContext context; - final T node; - final EditorState editorState; - - NodeWidgetContext({ - required this.context, - required this.node, - required this.editorState, - }); - - NodeWidgetContext copyWith({ - BuildContext? context, - T? node, - EditorState? editorState, - }) { - return NodeWidgetContext( - context: context ?? this.context, - node: node ?? this.node, - editorState: editorState ?? this.editorState, - ); - } -} - -class AppFlowyRenderPlugin extends AppFlowyRenderPluginService { - final Positioned Function(BuildContext context, List items)? - customActionMenuBuilder; - - AppFlowyRenderPlugin({ - required this.editorState, - required NodeWidgetBuilders builders, - this.customActionMenuBuilder, - }) { - registerAll(builders); - } - - final NodeWidgetBuilders _builders = {}; - final EditorState editorState; - - @override - Widget buildPluginWidget(NodeWidgetContext context) { - final node = context.node; - final name = - node.subtype == null ? node.type : '${node.type}/${node.subtype!}'; - final builder = _builders[name]; - if (builder != null && builder.nodeValidator(node)) { - return _autoUpdateNodeWidget(builder, context); - } else { - // Returns a SizeBox with 0 height if no builder found. - assert( - false, - 'No builder found for node(${node.id}, attributes(${node.attributes})})', - ); - return SizedBox( - key: node.key, - height: 0, - ); - } - } - - @override - void register(String name, NodeWidgetBuilder builder) { - Log.editor.info('registers plugin($name)...'); - _validatePlugin(name); - _builders[name] = builder; - } - - @override - void registerAll(Map builders) { - builders.forEach(register); - } - - @override - void unRegister(String name) { - _validatePlugin(name); - _builders.remove(name); - } - - @override - NodeWidgetBuilder? getBuilder(String name) { - return _builders[name]; - } - - Widget _autoUpdateNodeWidget( - NodeWidgetBuilder builder, NodeWidgetContext context) { - Widget notifier; - if (context.node is TextNode) { - notifier = ChangeNotifierProvider.value( - value: context.node as TextNode, - builder: (_, child) { - return Consumer( - builder: ((_, value, child) { - Log.ui.debug('TextNode is rebuilding...'); - return _buildWithActions(builder, context); - }), - ); - }); - } else { - notifier = ChangeNotifierProvider.value( - value: context.node, - builder: (_, child) { - return Consumer( - builder: ((_, value, child) { - Log.ui.debug('Node is rebuilding...'); - return _buildWithActions(builder, context); - }), - ); - }); - } - return CompositedTransformTarget( - link: context.node.layerLink, - child: notifier, - ); - } - - Widget _buildWithActions( - NodeWidgetBuilder builder, NodeWidgetContext context) { - if (builder is ActionProvider) { - return ChangeNotifierProvider( - create: (_) => ActionMenuState(context.node.path), - child: ActionMenuOverlay( - items: builder.actions(context), - customActionMenuBuilder: customActionMenuBuilder, - child: builder.build(context), - ), - ); - } else { - return builder.build(context); - } - } - - void _validatePlugin(String name) { - final paths = name.split('/'); - if (paths.length > 2) { - throw Exception('Plugin name must contain at most one or zero slash'); - } - if (_builders.containsKey(name)) { - throw Exception('Plugin name($name) already exists.'); - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/scroll_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/scroll_service.dart deleted file mode 100644 index 73d463b3b7..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/scroll_service.dart +++ /dev/null @@ -1,145 +0,0 @@ -import 'package:appflowy_editor/src/infra/log.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/object_extensions.dart'; - -/// [AppFlowyScrollService] is responsible for processing document scrolling. -/// -/// Usually, this service can be obtained by the following code. -/// ```dart -/// final keyboardService = editorState.service.scrollService; -/// ``` -/// -abstract class AppFlowyScrollService { - /// Returns the offset of the current document on the vertical axis. - double get dy; - - /// Returns the height of the current document. - double? get onePageHeight; - - /// Returns the number of pages in the current document. - int? get page; - - /// Returns the maximum scroll height on the vertical axis. - double get maxScrollExtent; - - /// Returns the minimum scroll height on the vertical axis. - double get minScrollExtent; - - /// Scrolls to the specified position. - /// - /// This function will filter illegal values. - /// Only within the range of minScrollExtent and maxScrollExtent are legal values. - void scrollTo(double dy); - - /// Enables scroll service. - void enable(); - - /// Disables scroll service. - /// - /// In some cases, you can disable scroll service of flowy_editor - /// when your custom component appears, - /// - /// But you need to call the `enable` function to restore after exiting - /// your custom component, otherwise the scroll service will fails. - void disable(); -} - -class AppFlowyScroll extends StatefulWidget { - const AppFlowyScroll({ - Key? key, - required this.child, - }) : super(key: key); - - final Widget child; - - @override - State createState() => _AppFlowyScrollState(); -} - -class _AppFlowyScrollState extends State - implements AppFlowyScrollService { - final _scrollController = ScrollController(); - final _scrollViewKey = GlobalKey(); - - bool _scrollEnabled = true; - - @override - double get dy => _scrollController.position.pixels; - - @override - double? get onePageHeight { - final renderBox = context.findRenderObject()?.unwrapOrNull(); - return renderBox?.size.height; - } - - @override - double get maxScrollExtent => _scrollController.position.maxScrollExtent; - - @override - double get minScrollExtent => _scrollController.position.minScrollExtent; - - @override - int? get page { - if (onePageHeight != null) { - final scrollExtent = maxScrollExtent - minScrollExtent; - return (scrollExtent / onePageHeight!).ceil(); - } - return null; - } - - @override - Widget build(BuildContext context) { - return Listener( - onPointerSignal: _onPointerSignal, - onPointerPanZoomUpdate: _onPointerPanZoomUpdate, - child: CustomScrollView( - key: _scrollViewKey, - physics: const NeverScrollableScrollPhysics(), - controller: _scrollController, - slivers: [ - SliverFillRemaining( - hasScrollBody: false, - child: widget.child, - ) - ], - ), - ); - } - - @override - void scrollTo(double dy) { - _scrollController.position.jumpTo( - dy.clamp( - _scrollController.position.minScrollExtent, - _scrollController.position.maxScrollExtent, - ), - ); - } - - @override - void disable() { - _scrollEnabled = false; - Log.scroll.debug('disable scroll service'); - } - - @override - void enable() { - _scrollEnabled = true; - Log.scroll.debug('enable scroll service'); - } - - void _onPointerSignal(PointerSignalEvent event) { - if (event is PointerScrollEvent && _scrollEnabled) { - final dy = (_scrollController.position.pixels + event.scrollDelta.dy); - scrollTo(dy); - } - } - - void _onPointerPanZoomUpdate(PointerPanZoomUpdateEvent event) { - if (_scrollEnabled) { - final dy = (_scrollController.position.pixels - event.panDelta.dy); - scrollTo(dy); - } - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/selection/selection_gesture.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/selection/selection_gesture.dart deleted file mode 100644 index 09cc624b7e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/selection/selection_gesture.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -/// Because the flutter's [DoubleTapGestureRecognizer] will block the [TapGestureRecognizer] -/// for a while. So we need to implement our own GestureDetector. -@immutable -class SelectionGestureDetector extends StatefulWidget { - const SelectionGestureDetector({ - Key? key, - this.child, - this.onTapDown, - this.onDoubleTapDown, - this.onTripleTapDown, - this.onSecondaryTapDown, - this.onPanStart, - this.onPanUpdate, - this.onPanEnd, - }) : super(key: key); - - @override - State createState() => - SelectionGestureDetectorState(); - - final Widget? child; - - final GestureTapDownCallback? onTapDown; - final GestureTapDownCallback? onDoubleTapDown; - final GestureTapDownCallback? onTripleTapDown; - final GestureTapDownCallback? onSecondaryTapDown; - final GestureDragStartCallback? onPanStart; - final GestureDragUpdateCallback? onPanUpdate; - final GestureDragEndCallback? onPanEnd; -} - -class SelectionGestureDetectorState extends State { - bool _isDoubleTap = false; - Timer? _doubleTapTimer; - int _tripleTabCount = 0; - Timer? _tripleTabTimer; - - final kTripleTapTimeout = const Duration(milliseconds: 500); - - @override - Widget build(BuildContext context) { - return RawGestureDetector( - behavior: HitTestBehavior.translucent, - gestures: { - PanGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => PanGestureRecognizer(supportedDevices: { - // https://docs.flutter.dev/release/breaking-changes/trackpad-gestures#for-gesture-interactions-not-suitable-for-trackpad-usage - // Exclude PointerDeviceKind.trackpad. - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - PointerDeviceKind.stylus, - PointerDeviceKind.invertedStylus, - }), - (recognizer) { - recognizer - ..onStart = widget.onPanStart - ..onUpdate = widget.onPanUpdate - ..onEnd = widget.onPanEnd; - }, - ), - TapGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => TapGestureRecognizer(), - (recognizer) { - recognizer.onTapDown = _tapDownDelegate; - recognizer.onSecondaryTapDown = widget.onSecondaryTapDown; - }, - ), - }, - child: widget.child, - ); - } - - _tapDownDelegate(TapDownDetails tapDownDetails) { - if (_tripleTabCount == 2) { - _tripleTabCount = 0; - _tripleTabTimer?.cancel(); - _tripleTabTimer = null; - if (widget.onTripleTapDown != null) { - widget.onTripleTapDown!(tapDownDetails); - } - } else if (_isDoubleTap) { - _isDoubleTap = false; - _doubleTapTimer?.cancel(); - _doubleTapTimer = null; - if (widget.onDoubleTapDown != null) { - widget.onDoubleTapDown!(tapDownDetails); - } - _tripleTabCount++; - } else { - if (widget.onTapDown != null) { - widget.onTapDown!(tapDownDetails); - } - - _isDoubleTap = true; - _doubleTapTimer?.cancel(); - _doubleTapTimer = Timer(kDoubleTapTimeout, () { - _isDoubleTap = false; - _doubleTapTimer = null; - }); - - _tripleTabCount = 1; - _tripleTabTimer?.cancel(); - _tripleTabTimer = Timer(kTripleTapTimeout, () { - _tripleTabCount = 0; - _tripleTabTimer = null; - }); - } - } - - @override - void dispose() { - _doubleTapTimer?.cancel(); - _tripleTabTimer?.cancel(); - super.dispose(); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/selection_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/selection_service.dart deleted file mode 100644 index 7578407a09..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/selection_service.dart +++ /dev/null @@ -1,727 +0,0 @@ -import 'dart:async'; - -import 'package:appflowy_editor/src/flutter/overlay.dart'; -import 'package:appflowy_editor/src/infra/log.dart'; -import 'package:appflowy_editor/src/service/context_menu/built_in_context_menu_item.dart'; -import 'package:appflowy_editor/src/service/context_menu/context_menu.dart'; -import 'package:flutter/material.dart' hide Overlay, OverlayEntry; - -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:appflowy_editor/src/core/document/node_iterator.dart'; -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/extensions/node_extensions.dart'; -import 'package:appflowy_editor/src/extensions/object_extensions.dart'; -import 'package:appflowy_editor/src/render/selection/cursor_widget.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:appflowy_editor/src/render/selection/selection_widget.dart'; -import 'package:appflowy_editor/src/service/selection/selection_gesture.dart'; - -/// [AppFlowySelectionService] is responsible for processing -/// the [Selection] changes and updates. -/// -/// Usually, this service can be obtained by the following code. -/// ```dart -/// final selectionService = editorState.service.selectionService; -/// -/// /** get current selection value*/ -/// final selection = selectionService.currentSelection.value; -/// -/// /** get current selected nodes*/ -/// final nodes = selectionService.currentSelectedNodes; -/// ``` -/// -abstract class AppFlowySelectionService { - /// The current [Selection] in editor. - /// - /// The value is null if there is no nodes are selected. - ValueNotifier get currentSelection; - - /// The current selected [Node]s in editor. - /// - /// The order of the result is determined according to the [currentSelection]. - /// The result are ordered from back to front if the selection is forward. - /// The result are ordered from front to back if the selection is backward. - /// - /// For example, Here is an array of selected nodes, `[n1, n2, n3]`. - /// The result will be `[n3, n2, n1]` if the selection is forward, - /// and `[n1, n2, n3]` if the selection is backward. - /// - /// Returns empty result if there is no nodes are selected. - List get currentSelectedNodes; - - /// Updates the selection. - /// - /// The editor will update selection area and toolbar area - /// if the [selection] is not collapsed, - /// otherwise, will update the cursor area. - void updateSelection(Selection? selection); - - /// Clears the selection area, cursor area and the popup list area. - void clearSelection(); - - /// Clears the cursor area. - void clearCursor(); - - /// Returns the [Node]s in [Selection]. - List getNodesInSelection(Selection selection); - - /// Returns the [Node] containing to the [offset]. - /// - /// [offset] must be under the global coordinate system. - Node? getNodeInOffset(Offset offset); - - /// Returns the [Position] closest to the [offset]. - /// - /// Returns null if there is no nodes are selected. - /// - /// [offset] must be under the global coordinate system. - Position? getPositionInOffset(Offset offset); - - /// The current selection areas's rect in editor. - List get selectionRects; - - void register(SelectionInterceptor interceptor); - void unRegister(SelectionInterceptor interceptor); -} - -class SelectionInterceptor { - bool Function(TapDownDetails details)? canTap; -} - -class AppFlowySelection extends StatefulWidget { - const AppFlowySelection({ - Key? key, - this.cursorColor = const Color(0xFF00BCF0), - this.selectionColor = const Color.fromARGB(53, 111, 201, 231), - this.editable = true, - required this.editorState, - required this.child, - }) : super(key: key); - - final EditorState editorState; - final Widget child; - final Color cursorColor; - final Color selectionColor; - final bool editable; - - @override - State createState() => _AppFlowySelectionState(); -} - -class _AppFlowySelectionState extends State - with WidgetsBindingObserver - implements AppFlowySelectionService { - final _cursorKey = GlobalKey(debugLabel: 'cursor'); - - @override - final List selectionRects = []; - final List _selectionAreas = []; - final List _cursorAreas = []; - final List _contextMenuAreas = []; - - // OverlayEntry? _debugOverlay; - - /// Pan - Offset? _panStartOffset; - double? _panStartScrollDy; - - EditorState get editorState => widget.editorState; - - // Toolbar - Timer? _toolbarTimer; - - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addObserver(this); - currentSelection.addListener(_onSelectionChange); - } - - @override - void didChangeMetrics() { - super.didChangeMetrics(); - - // Need to refresh the selection when the metrics changed. - if (currentSelection.value != null) { - updateSelection(currentSelection.value!); - } - } - - @override - void dispose() { - clearSelection(); - WidgetsBinding.instance.removeObserver(this); - currentSelection.removeListener(_onSelectionChange); - _clearToolbar(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - if (!widget.editable) { - return Container( - child: widget.child, - ); - } else { - return SelectionGestureDetector( - onPanStart: _onPanStart, - onPanUpdate: _onPanUpdate, - onPanEnd: _onPanEnd, - onTapDown: _onTapDown, - onSecondaryTapDown: _onSecondaryTapDown, - onDoubleTapDown: _onDoubleTapDown, - onTripleTapDown: _onTripleTapDown, - child: widget.child, - ); - } - } - - @override - ValueNotifier currentSelection = ValueNotifier(null); - - @override - List currentSelectedNodes = []; - - @override - List getNodesInSelection(Selection selection) { - final start = - selection.isBackward ? selection.start.path : selection.end.path; - final end = - selection.isBackward ? selection.end.path : selection.start.path; - assert(start <= end); - final startNode = editorState.document.nodeAtPath(start); - final endNode = editorState.document.nodeAtPath(end); - if (startNode != null && endNode != null) { - final nodes = NodeIterator( - document: editorState.document, - startNode: startNode, - endNode: endNode, - ).toList(); - if (selection.isBackward) { - return nodes; - } else { - return nodes.reversed.toList(growable: false); - } - } - return []; - } - - @override - void updateSelection(Selection? selection) { - if (!widget.editable) { - return; - } - - selectionRects.clear(); - clearSelection(); - _clearToolbar(); - - if (selection != null) { - if (selection.isCollapsed) { - // updates cursor area. - Log.selection.debug('update cursor area, $selection'); - _updateCursorAreas(selection.start); - } else { - // updates selection area. - Log.selection.debug('update cursor area, $selection'); - _updateSelectionAreas(selection); - } - } - - currentSelection.value = selection; - editorState.updateCursorSelection(selection, CursorUpdateReason.uiEvent); - } - - @override - void clearSelection() { - currentSelectedNodes = []; - currentSelection.value = null; - - clearCursor(); - // clear selection areas - _selectionAreas - ..forEach((overlay) => overlay.remove()) - ..clear(); - // clear cursor areas - - // hide toolbar - // editorState.service.toolbarService?.hide(); - - // clear context menu - _clearContextMenu(); - } - - @override - void clearCursor() { - // clear cursor areas - _cursorAreas - ..forEach((overlay) => overlay.remove()) - ..clear(); - } - - void _clearContextMenu() { - _contextMenuAreas - ..forEach((overlay) => overlay.remove()) - ..clear(); - } - - @override - Node? getNodeInOffset(Offset offset) { - final sortedNodes = - editorState.document.root.children.toList(growable: false); - return _getNodeInOffset( - sortedNodes, - offset, - 0, - sortedNodes.length - 1, - ); - } - - @override - Position? getPositionInOffset(Offset offset) { - final node = getNodeInOffset(offset); - final selectable = node?.selectable; - if (selectable == null) { - clearSelection(); - return null; - } - return selectable.getPositionInOffset(offset); - } - - void _onTapDown(TapDownDetails details) { - final canTap = - _interceptors.every((element) => element.canTap?.call(details) ?? true); - if (!canTap) return; - - // clear old state. - _panStartOffset = null; - - final position = getPositionInOffset(details.globalPosition); - if (position == null) { - return; - } - final selection = Selection.collapsed(position); - updateSelection(selection); - - _enableInteraction(); - - _showDebugLayerIfNeeded(offset: details.globalPosition); - } - - void _onDoubleTapDown(TapDownDetails details) { - final offset = details.globalPosition; - final node = getNodeInOffset(offset); - final selection = node?.selectable?.getWordBoundaryInOffset(offset); - if (selection == null) { - clearSelection(); - return; - } - updateSelection(selection); - - _enableInteraction(); - } - - void _onTripleTapDown(TapDownDetails details) { - final offset = details.globalPosition; - final node = getNodeInOffset(offset); - final selectable = node?.selectable; - if (selectable == null) { - clearSelection(); - return; - } - Selection selection = Selection( - start: selectable.start(), - end: selectable.end(), - ); - updateSelection(selection); - - _enableInteraction(); - } - - void _onSecondaryTapDown(TapDownDetails details) { - // if selection is null, or - // selection.isCollapsedand and the selected node is TextNode. - // try to select the word. - final selection = currentSelection.value; - if (selection == null || - (selection.isCollapsed == true && - currentSelectedNodes.first is TextNode)) { - _onDoubleTapDown(details); - } - - _showContextMenu(details); - } - - void _onPanStart(DragStartDetails details) { - clearSelection(); - _clearToolbar(); - - _panStartOffset = details.globalPosition.translate(-3.0, 0); - _panStartScrollDy = editorState.service.scrollService?.dy; - - _enableInteraction(); - } - - void _onPanUpdate(DragUpdateDetails details) { - if (_panStartOffset == null || _panStartScrollDy == null) { - return; - } - - _enableInteraction(); - - final panEndOffset = details.globalPosition; - final dy = editorState.service.scrollService?.dy; - final panStartOffset = dy == null - ? _panStartOffset! - : _panStartOffset!.translate(0, _panStartScrollDy! - dy); - - final first = getNodeInOffset(panStartOffset)?.selectable; - final last = getNodeInOffset(panEndOffset)?.selectable; - - // compute the selection in range. - if (first != null && last != null) { - Log.selection.debug('first = $first, last = $last'); - final start = - first.getSelectionInRange(panStartOffset, panEndOffset).start; - final end = last.getSelectionInRange(panStartOffset, panEndOffset).end; - final selection = Selection(start: start, end: end); - updateSelection(selection); - } - - _showDebugLayerIfNeeded(offset: panEndOffset); - } - - void _onPanEnd(DragEndDetails details) { - // do nothing - } - - void _updateSelectionAreas(Selection selection) { - final nodes = getNodesInSelection(selection); - - currentSelectedNodes = nodes; - - // TODO: need to be refactored. - Offset? toolbarOffset; - Alignment? alignment; - LayerLink? layerLink; - final editorOffset = - editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; - final editorSize = editorState.renderBox?.size ?? Size.zero; - - final backwardNodes = - selection.isBackward ? nodes : nodes.reversed.toList(growable: false); - final normalizedSelection = selection.normalized; - assert(normalizedSelection.isBackward); - - Log.selection.debug('update selection areas, $normalizedSelection'); - - for (var i = 0; i < backwardNodes.length; i++) { - final node = backwardNodes[i]; - final selectable = node.selectable; - if (selectable == null) { - continue; - } - - var newSelection = normalizedSelection.copyWith(); - - /// In the case of multiple selections, - /// we need to return a new selection for each selected node individually. - /// - /// < > means selected. - /// text: abcdopqr - /// - if (!normalizedSelection.isSingle) { - if (i == 0) { - newSelection = newSelection.copyWith(end: selectable.end()); - } else if (i == nodes.length - 1) { - newSelection = newSelection.copyWith(start: selectable.start()); - } else { - newSelection = Selection( - start: selectable.start(), - end: selectable.end(), - ); - } - } - - const baseToolbarOffset = Offset(0, 35.0); - final rects = selectable.getRectsInSelection(newSelection); - for (final rect in rects) { - final selectionRect = _transformRectToGlobal(selectable, rect); - selectionRects.add(selectionRect); - - // TODO: Need to compute more precise location. - if ((selectionRect.topLeft.dy - editorOffset.dy) <= - baseToolbarOffset.dy) { - if (selectionRect.topLeft.dx <= - editorSize.width / 3.0 + editorOffset.dx) { - toolbarOffset ??= rect.bottomLeft; - alignment ??= Alignment.topLeft; - } else if (selectionRect.topRight.dx >= - editorSize.width * 2.0 / 3.0 + editorOffset.dx) { - toolbarOffset ??= rect.bottomRight; - alignment ??= Alignment.topRight; - } else { - toolbarOffset ??= rect.bottomCenter; - alignment ??= Alignment.topCenter; - } - } else { - if (selectionRect.topLeft.dx <= - editorSize.width / 3.0 + editorOffset.dx) { - toolbarOffset ??= rect.topLeft - baseToolbarOffset; - alignment ??= Alignment.topLeft; - } else if (selectionRect.topRight.dx >= - editorSize.width * 2.0 / 3.0 + editorOffset.dx) { - toolbarOffset ??= rect.topRight - baseToolbarOffset; - alignment ??= Alignment.topRight; - } else { - toolbarOffset ??= rect.topCenter - baseToolbarOffset; - alignment ??= Alignment.topCenter; - } - } - - layerLink ??= node.layerLink; - - final overlay = OverlayEntry( - builder: (context) => SelectionWidget( - color: widget.selectionColor, - layerLink: node.layerLink, - rect: rect, - ), - ); - _selectionAreas.add(overlay); - } - } - - Overlay.of(context)?.insertAll(_selectionAreas); - - // show toolbar - _showToolbarWithDelay(toolbarOffset, layerLink, alignment!); - } - - void _updateCursorAreas(Position position) { - final node = editorState.document.root.childAtPath(position.path); - - if (node == null) { - assert(false); - return; - } - - currentSelectedNodes = [node]; - - _showCursor(node, position); - _clearToolbar(); - } - - void _showCursor(Node node, Position position) { - final selectable = node.selectable; - final cursorRect = selectable?.getCursorRectInPosition(position); - if (selectable != null && cursorRect != null) { - final cursorArea = OverlayEntry( - builder: (context) => CursorWidget( - key: _cursorKey, - rect: cursorRect, - color: widget.cursorColor, - layerLink: node.layerLink, - shouldBlink: selectable.shouldCursorBlink, - cursorStyle: selectable.cursorStyle, - ), - ); - - _cursorAreas.add(cursorArea); - selectionRects.add(_transformRectToGlobal(selectable, cursorRect)); - Overlay.of(context)?.insertAll(_cursorAreas); - - _forceShowCursor(); - } - } - - void _forceShowCursor() { - _cursorKey.currentState?.unwrapOrNull()?.show(); - } - - void _showContextMenu(TapDownDetails details) { - _clearContextMenu(); - - // For now, only support the text node. - if (!currentSelectedNodes.every((element) => element is TextNode)) { - return; - } - - final baseOffset = - editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; - final offset = details.globalPosition + const Offset(10, 10) - baseOffset; - final contextMenu = OverlayEntry( - builder: (context) => ContextMenu( - position: offset, - editorState: editorState, - items: builtInContextMenuItems, - onPressed: () => _clearContextMenu(), - ), - ); - - _contextMenuAreas.add(contextMenu); - Overlay.of(context)?.insert(contextMenu); - } - - void _scrollUpOrDownIfNeeded() { - final dy = editorState.service.scrollService?.dy; - final selectNodes = currentSelectedNodes; - final selection = currentSelection.value; - if (dy == null || selection == null || selectNodes.isEmpty) { - return; - } - - final rect = selectNodes.last.rect; - - final size = MediaQuery.of(context).size.height; - final topLimit = size * 0.3; - final bottomLimit = size * 0.8; - - // TODO: It is necessary to calculate the relative speed - // according to the gap and move forward more gently. - if (rect.top >= bottomLimit) { - if (selection.isSingle) { - editorState.service.scrollService?.scrollTo(dy + size * 0.2); - } else if (selection.isBackward) { - editorState.service.scrollService?.scrollTo(dy + 10.0); - } - } else if (rect.bottom <= topLimit) { - if (selection.isForward) { - editorState.service.scrollService?.scrollTo(dy - 10.0); - } - } - } - - Node? _getNodeInOffset( - List sortedNodes, Offset offset, int start, int end) { - if (start < 0 && end >= sortedNodes.length) { - return null; - } - var min = start; - var max = end; - while (min <= max) { - final mid = min + ((max - min) >> 1); - final rect = sortedNodes[mid].rect; - if (rect.bottom <= offset.dy) { - min = mid + 1; - } else { - max = mid - 1; - } - } - min = min.clamp(start, end); - final node = sortedNodes[min]; - if (node.children.isNotEmpty && node.children.first.rect.top <= offset.dy) { - final children = node.children.toList(growable: false); - return _getNodeInOffset( - children, - offset, - 0, - children.length - 1, - ); - } - return node; - } - - void _enableInteraction() { - editorState.service.keyboardService?.enable(); - editorState.service.scrollService?.enable(); - } - - Rect _transformRectToGlobal(SelectableMixin selectable, Rect r) { - final Offset topLeft = selectable.localToGlobal(Offset(r.left, r.top)); - return Rect.fromLTWH(topLeft.dx, topLeft.dy, r.width, r.height); - } - - void _onSelectionChange() { - _scrollUpOrDownIfNeeded(); - } - - void _showToolbarWithDelay( - Offset? toolbarOffset, - LayerLink? layerLink, - Alignment alignment, { - Duration delay = const Duration(milliseconds: 400), - }) { - if (toolbarOffset == null && layerLink == null) { - _clearToolbar(); - return; - } - if (_toolbarTimer?.isActive ?? false) { - _toolbarTimer?.cancel(); - } - _toolbarTimer = Timer( - delay, - () { - if (toolbarOffset != null && layerLink != null) { - editorState.service.toolbarService?.showInOffset( - toolbarOffset, - alignment, - layerLink, - ); - } - }, - ); - } - - void _clearToolbar() { - editorState.service.toolbarService?.hide(); - if (_toolbarTimer?.isActive ?? false) { - _toolbarTimer?.cancel(); - } - } - - void _showDebugLayerIfNeeded({Offset? offset}) { - // remove false to show debug overlay. - // if (kDebugMode && false) { - // _debugOverlay?.remove(); - // if (offset != null) { - // _debugOverlay = OverlayEntry( - // builder: (context) => Positioned.fromRect( - // rect: Rect.fromPoints(offset, offset.translate(20, 20)), - // child: Container( - // color: Colors.red.withOpacity(0.2), - // ), - // ), - // ); - // Overlay.of(context)?.insert(_debugOverlay!); - // } else if (_panStartOffset != null) { - // _debugOverlay = OverlayEntry( - // builder: (context) => Positioned.fromRect( - // rect: Rect.fromPoints( - // _panStartOffset?.translate( - // 0, - // -(editorState.service.scrollService!.dy - - // _panStartScrollDy!), - // ) ?? - // Offset.zero, - // offset ?? Offset.zero), - // child: Container( - // color: Colors.red.withOpacity(0.2), - // ), - // ), - // ); - // Overlay.of(context)?.insert(_debugOverlay!); - // } else { - // _debugOverlay = null; - // } - // } - } - - final List _interceptors = []; - @override - void register(SelectionInterceptor interceptor) { - _interceptors.add(interceptor); - } - - @override - void unRegister(SelectionInterceptor interceptor) { - _interceptors.removeWhere((element) => element == interceptor); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/service.dart deleted file mode 100644 index 7b64e9e36d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/service.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:appflowy_editor/src/service/input_service.dart'; -import 'package:appflowy_editor/src/service/keyboard_service.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:appflowy_editor/src/service/scroll_service.dart'; -import 'package:appflowy_editor/src/service/selection_service.dart'; -import 'package:appflowy_editor/src/service/toolbar_service.dart'; -import 'package:flutter/material.dart'; - -class FlowyService { - // selection service - final selectionServiceKey = GlobalKey(debugLabel: 'flowy_selection_service'); - AppFlowySelectionService get selectionService { - assert(selectionServiceKey.currentState != null && - selectionServiceKey.currentState is AppFlowySelectionService); - return selectionServiceKey.currentState! as AppFlowySelectionService; - } - - // keyboard service - final keyboardServiceKey = GlobalKey(debugLabel: 'flowy_keyboard_service'); - AppFlowyKeyboardService? get keyboardService { - if (keyboardServiceKey.currentState != null && - keyboardServiceKey.currentState is AppFlowyKeyboardService) { - return keyboardServiceKey.currentState! as AppFlowyKeyboardService; - } - return null; - } - - // input service - final inputServiceKey = GlobalKey(debugLabel: 'flowy_input_service'); - AppFlowyInputService? get inputService { - if (inputServiceKey.currentState != null && - inputServiceKey.currentState is AppFlowyInputService) { - return inputServiceKey.currentState! as AppFlowyInputService; - } - return null; - } - - // render plugin service - late AppFlowyRenderPlugin renderPluginService; - - // toolbar service - final toolbarServiceKey = GlobalKey(debugLabel: 'flowy_toolbar_service'); - AppFlowyToolbarService? get toolbarService { - if (toolbarServiceKey.currentState != null && - toolbarServiceKey.currentState is AppFlowyToolbarService) { - return toolbarServiceKey.currentState! as AppFlowyToolbarService; - } - return null; - } - - // scroll service - final scrollServiceKey = GlobalKey(debugLabel: 'flowy_scroll_service'); - AppFlowyScrollService? get scrollService { - if (scrollServiceKey.currentState != null && - scrollServiceKey.currentState is AppFlowyScrollService) { - return scrollServiceKey.currentState! as AppFlowyScrollService; - } - return null; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart deleted file mode 100644 index 222b412e7c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart +++ /dev/null @@ -1,335 +0,0 @@ -import 'package:appflowy_editor/src/service/internal_key_event_handlers/arrow_keys_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/backspace_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/copy_paste_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/exit_editing_mode_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/page_up_down_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/redo_undo_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/select_all_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/slash_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/format_style_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/space_on_web_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/tab_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/whitespace_handler.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/checkbox_event_handler.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event.dart'; -import 'package:flutter/foundation.dart'; - -List builtInShortcutEvents = [ - ShortcutEvent( - key: 'Move cursor up', - command: 'arrow up', - handler: cursorUp, - ), - ShortcutEvent( - key: 'Move cursor down', - command: 'arrow down', - handler: cursorDown, - ), - ShortcutEvent( - key: 'Move cursor left', - command: 'arrow left', - handler: cursorLeft, - ), - ShortcutEvent( - key: 'Move cursor right', - command: 'arrow right', - handler: cursorRight, - ), - ShortcutEvent( - key: 'Cursor up select', - command: 'shift+arrow up', - handler: cursorUpSelect, - ), - ShortcutEvent( - key: 'Cursor down select', - command: 'shift+arrow down', - handler: cursorDownSelect, - ), - ShortcutEvent( - key: 'Cursor left word select', - command: 'shift+alt+arrow left', - windowsCommand: 'shift+alt+arrow left', - linuxCommand: 'shift+alt+arrow left', - handler: cursorLeftWordSelect, - ), - ShortcutEvent( - key: 'Cursor right word select', - command: 'shift+alt+arrow right', - windowsCommand: 'shift+alt+arrow right', - linuxCommand: 'shift+alt+arrow right', - handler: cursorRightWordSelect, - ), - ShortcutEvent( - key: 'Cursor word delete', - command: 'meta+backspace', - windowsCommand: 'ctrl+backspace', - linuxCommand: 'ctrl+backspace', - handler: cursorLeftWordDelete, - ), - ShortcutEvent( - key: 'Cursor left select', - command: 'shift+arrow left', - handler: cursorLeftSelect, - ), - ShortcutEvent( - key: 'Cursor right select', - command: 'shift+arrow right', - handler: cursorRightSelect, - ), - ShortcutEvent( - key: 'Move cursor top', - command: 'meta+arrow up', - windowsCommand: 'ctrl+arrow up', - linuxCommand: 'ctrl+arrow up', - handler: cursorTop, - ), - ShortcutEvent( - key: 'Move cursor bottom', - command: 'meta+arrow down', - windowsCommand: 'ctrl+arrow down', - linuxCommand: 'ctrl+arrow down', - handler: cursorBottom, - ), - ShortcutEvent( - key: 'Move cursor begin', - command: 'meta+arrow left', - windowsCommand: 'ctrl+arrow left', - linuxCommand: 'ctrl+arrow left', - handler: cursorBegin, - ), - ShortcutEvent( - key: 'Move cursor end', - command: 'meta+arrow right', - windowsCommand: 'ctrl+arrow right', - linuxCommand: 'ctrl+arrow right', - handler: cursorEnd, - ), - ShortcutEvent( - key: 'Cursor top select', - command: 'meta+shift+arrow up', - windowsCommand: 'ctrl+shift+arrow up', - linuxCommand: 'ctrl+shift+arrow up', - handler: cursorTopSelect, - ), - ShortcutEvent( - key: 'Cursor bottom select', - command: 'meta+shift+arrow down', - windowsCommand: 'ctrl+shift+arrow down', - linuxCommand: 'ctrl+shift+arrow down', - handler: cursorBottomSelect, - ), - ShortcutEvent( - key: 'Cursor begin select', - command: 'meta+shift+arrow left,shift+home', - windowsCommand: 'ctrl+shift+arrow left,shift+home', - linuxCommand: 'ctrl+shift+arrow left,shift+home', - handler: cursorBeginSelect, - ), - ShortcutEvent( - key: 'Cursor end select', - command: 'meta+shift+arrow right,shift+end', - windowsCommand: 'ctrl+shift+arrow right,shift+end', - linuxCommand: 'ctrl+shift+arrow right,shift+end', - handler: cursorEndSelect, - ), - ShortcutEvent( - key: 'Redo', - command: 'meta+shift+z,meta+y', - windowsCommand: 'ctrl+shift+z,ctrl+y', - linuxCommand: 'ctrl+shift+z,ctrl+y', - handler: redoEventHandler, - ), - ShortcutEvent( - key: 'Undo', - command: 'meta+z', - windowsCommand: 'ctrl+z', - linuxCommand: 'ctrl+z', - handler: undoEventHandler, - ), - ShortcutEvent( - key: 'Format bold', - command: 'meta+b', - windowsCommand: 'ctrl+b', - linuxCommand: 'ctrl+b', - handler: formatBoldEventHandler, - ), - ShortcutEvent( - key: 'Format italic', - command: 'meta+i', - windowsCommand: 'ctrl+i', - linuxCommand: 'ctrl+i', - handler: formatItalicEventHandler, - ), - ShortcutEvent( - key: 'Format underline', - command: 'meta+u', - windowsCommand: 'ctrl+u', - linuxCommand: 'ctrl+u', - handler: formatUnderlineEventHandler, - ), - ShortcutEvent( - key: 'Toggle Checkbox', - command: 'meta+enter', - windowsCommand: 'ctrl+enter', - linuxCommand: 'ctrl+enter', - handler: toggleCheckbox, - ), - ShortcutEvent( - key: 'Format strikethrough', - command: 'meta+shift+s', - windowsCommand: 'ctrl+shift+s', - linuxCommand: 'ctrl+shift+s', - handler: formatStrikethroughEventHandler, - ), - ShortcutEvent( - key: 'Format highlight', - command: 'meta+shift+h', - windowsCommand: 'ctrl+shift+h', - linuxCommand: 'ctrl+shift+h', - handler: formatHighlightEventHandler, - ), - ShortcutEvent( - key: 'Format embed code', - command: 'meta+e', - windowsCommand: 'ctrl+e', - linuxCommand: 'ctrl+e', - handler: formatEmbedCodeEventHandler, - ), - ShortcutEvent( - key: 'Format link', - command: 'meta+k', - windowsCommand: 'ctrl+k', - linuxCommand: 'ctrl+k', - handler: formatLinkEventHandler, - ), - ShortcutEvent( - key: 'Copy', - command: 'meta+c', - windowsCommand: 'ctrl+c', - linuxCommand: 'ctrl+c', - handler: copyEventHandler, - ), - ShortcutEvent( - key: 'Paste', - command: 'meta+v', - windowsCommand: 'ctrl+v', - linuxCommand: 'ctrl+v', - handler: pasteEventHandler, - ), - ShortcutEvent( - key: 'Cut', - command: 'meta+x', - windowsCommand: 'ctrl+x', - linuxCommand: 'ctrl+x', - handler: cutEventHandler, - ), - ShortcutEvent( - key: 'Home', - command: 'home', - handler: cursorBegin, - ), - ShortcutEvent( - key: 'End', - command: 'end', - handler: cursorEnd, - ), - ShortcutEvent( - key: 'Delete Text by backspace', - command: 'backspace', - handler: backspaceEventHandler, - ), - ShortcutEvent( - key: 'Delete Text', - command: 'delete', - handler: deleteEventHandler, - ), - ShortcutEvent( - key: 'selection menu', - character: '/', - handler: slashShortcutHandler, - ), - ShortcutEvent( - key: 'enter', - command: 'enter', - handler: enterWithoutShiftInTextNodesHandler, - ), - ShortcutEvent( - key: 'markdown', - command: 'space', - handler: whiteSpaceHandler, - ), - ShortcutEvent( - key: 'select all', - command: 'meta+a', - windowsCommand: 'ctrl+a', - linuxCommand: 'ctrl+a', - handler: selectAllHandler, - ), - ShortcutEvent( - key: 'Page up', - command: 'page up', - handler: pageUpHandler, - ), - ShortcutEvent( - key: 'Page down', - command: 'page down', - handler: pageDownHandler, - ), - ShortcutEvent( - key: 'Tab', - command: 'tab', - handler: tabHandler, - ), - ShortcutEvent( - key: 'Backquote to code', - command: 'backquote', - handler: backquoteToCodeHandler, - ), - ShortcutEvent( - key: 'Double tilde to strikethrough', - character: '~', - handler: doubleTildeToStrikethrough, - ), - ShortcutEvent( - key: 'Markdown link or image', - command: 'shift+parenthesis right', - handler: markdownLinkOrImageHandler, - ), - ShortcutEvent( - key: 'Exit editing mode', - command: 'escape', - handler: exitEditingModeEventHandler, - ), - ShortcutEvent( - key: 'Underscore to italic', - character: '_', - handler: underscoreToItalicHandler, - ), - ShortcutEvent( - key: 'Asterisk to italic', - character: '*', - handler: asteriskToItalicHandler, - ), - ShortcutEvent( - key: 'Double asterisk to bold', - character: '*', - handler: doubleAsteriskToBoldHandler, - ), - ShortcutEvent( - key: 'Double underscore to bold', - character: '_', - handler: doubleUnderscoreToBoldHandler, - ), - // https://github.com/flutter/flutter/issues/104944 - // Workaround: Using space editing on the web platform often results in errors, - // so adding a shortcut event to handle the space input instead of using the - // `input_service`. - if (kIsWeb) - ShortcutEvent( - key: 'Space on the Web', - command: 'space', - handler: spaceOnWebHandler, - ), -]; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/key_mapping.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/key_mapping.dart deleted file mode 100644 index bddbd09711..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/key_mapping.dart +++ /dev/null @@ -1,451 +0,0 @@ -/// Keyboard key to keycode mapping table -/// -/// Copy from flutter project, keyboard_key.dart. -/// - -Map keyToCodeMapping = { - 'Space': 0x00000000020, - 'Exclamation': 0x00000000021, - 'Quote': 0x00000000022, - 'Number Sign': 0x00000000023, - 'Dollar': 0x00000000024, - 'Percent': 0x00000000025, - 'Ampersand': 0x00000000026, - 'Quote Single': 0x00000000027, - 'Parenthesis Left': 0x00000000028, - 'Parenthesis Right': 0x00000000029, - 'Asterisk': 0x0000000002a, - 'Add': 0x0000000002b, - 'Comma': 0x0000000002c, - 'Minus': 0x0000000002d, - 'Period': 0x0000000002e, - 'Slash': 0x0000000002f, - 'Digit 0': 0x00000000030, - 'Digit 1': 0x00000000031, - 'Digit 2': 0x00000000032, - 'Digit 3': 0x00000000033, - 'Digit 4': 0x00000000034, - 'Digit 5': 0x00000000035, - 'Digit 6': 0x00000000036, - 'Digit 7': 0x00000000037, - 'Digit 8': 0x00000000038, - 'Digit 9': 0x00000000039, - 'Colon': 0x0000000003a, - 'Semicolon': 0x0000000003b, - 'Less': 0x0000000003c, - 'Equal': 0x0000000003d, - 'Greater': 0x0000000003e, - 'Question': 0x0000000003f, - 'At': 0x00000000040, - 'Bracket Left': 0x0000000005b, - 'Backslash': 0x0000000005c, - 'Bracket Right': 0x0000000005d, - 'Caret': 0x0000000005e, - 'Underscore': 0x0000000005f, - 'Backquote': 0x00000000060, - 'A': 0x00000000061, - 'B': 0x00000000062, - 'C': 0x00000000063, - 'D': 0x00000000064, - 'E': 0x00000000065, - 'F': 0x00000000066, - 'G': 0x00000000067, - 'H': 0x00000000068, - 'I': 0x00000000069, - 'J': 0x0000000006a, - 'K': 0x0000000006b, - 'L': 0x0000000006c, - 'M': 0x0000000006d, - 'N': 0x0000000006e, - 'O': 0x0000000006f, - 'P': 0x00000000070, - 'Q': 0x00000000071, - 'R': 0x00000000072, - 'S': 0x00000000073, - 'T': 0x00000000074, - 'U': 0x00000000075, - 'V': 0x00000000076, - 'W': 0x00000000077, - 'X': 0x00000000078, - 'Y': 0x00000000079, - 'Z': 0x0000000007a, - 'Brace Left': 0x0000000007b, - 'Bar': 0x0000000007c, - 'Brace Right': 0x0000000007d, - 'Tilde': 0x0000000007e, - 'Unidentified': 0x00100000001, - 'Backspace': 0x00100000008, - 'Tab': 0x00100000009, - 'Enter': 0x0010000000d, - 'Escape': 0x0010000001b, - 'Delete': 0x0010000007f, - 'Accel': 0x00100000101, - 'Alt Graph': 0x00100000103, - 'Caps Lock': 0x00100000104, - 'Fn': 0x00100000106, - 'Fn Lock': 0x00100000107, - 'Hyper': 0x00100000108, - 'Num Lock': 0x0010000010a, - 'Scroll Lock': 0x0010000010c, - 'Super': 0x0010000010e, - 'Symbol': 0x0010000010f, - 'Symbol Lock': 0x00100000110, - 'Shift Level 5': 0x00100000111, - 'Arrow Down': 0x00100000301, - 'Arrow Left': 0x00100000302, - 'Arrow Right': 0x00100000303, - 'Arrow Up': 0x00100000304, - 'End': 0x00100000305, - 'Home': 0x00100000306, - 'Page Down': 0x00100000307, - 'Page Up': 0x00100000308, - 'Clear': 0x00100000401, - 'Copy': 0x00100000402, - 'Cr Sel': 0x00100000403, - 'Cut': 0x00100000404, - 'Erase Eof': 0x00100000405, - 'Ex Sel': 0x00100000406, - 'Insert': 0x00100000407, - 'Paste': 0x00100000408, - 'Redo': 0x00100000409, - 'Undo': 0x0010000040a, - 'Accept': 0x00100000501, - 'Again': 0x00100000502, - 'Attn': 0x00100000503, - 'Cancel': 0x00100000504, - 'Context Menu': 0x00100000505, - 'Execute': 0x00100000506, - 'Find': 0x00100000507, - 'Help': 0x00100000508, - 'Pause': 0x00100000509, - 'Play': 0x0010000050a, - 'Props': 0x0010000050b, - 'Select': 0x0010000050c, - 'Zoom In': 0x0010000050d, - 'Zoom Out': 0x0010000050e, - 'Brightness Down': 0x00100000601, - 'Brightness Up': 0x00100000602, - 'Camera': 0x00100000603, - 'Eject': 0x00100000604, - 'Log Off': 0x00100000605, - 'Power': 0x00100000606, - 'Power Off': 0x00100000607, - 'Print Screen': 0x00100000608, - 'Hibernate': 0x00100000609, - 'Standby': 0x0010000060a, - 'Wake Up': 0x0010000060b, - 'All Candidates': 0x00100000701, - 'Alphanumeric': 0x00100000702, - 'Code Input': 0x00100000703, - 'Compose': 0x00100000704, - 'Convert': 0x00100000705, - 'Final Mode': 0x00100000706, - 'Group First': 0x00100000707, - 'Group Last': 0x00100000708, - 'Group Next': 0x00100000709, - 'Group Previous': 0x0010000070a, - 'Mode Change': 0x0010000070b, - 'Next Candidate': 0x0010000070c, - 'Non Convert': 0x0010000070d, - 'Previous Candidate': 0x0010000070e, - 'Process': 0x0010000070f, - 'Single Candidate': 0x00100000710, - 'Hangul Mode': 0x00100000711, - 'Hanja Mode': 0x00100000712, - 'Junja Mode': 0x00100000713, - 'Eisu': 0x00100000714, - 'Hankaku': 0x00100000715, - 'Hiragana': 0x00100000716, - 'Hiragana Katakana': 0x00100000717, - 'Kana Mode': 0x00100000718, - 'Kanji Mode': 0x00100000719, - 'Katakana': 0x0010000071a, - 'Romaji': 0x0010000071b, - 'Zenkaku': 0x0010000071c, - 'Zenkaku Hankaku': 0x0010000071d, - 'F1': 0x00100000801, - 'F2': 0x00100000802, - 'F3': 0x00100000803, - 'F4': 0x00100000804, - 'F5': 0x00100000805, - 'F6': 0x00100000806, - 'F7': 0x00100000807, - 'F8': 0x00100000808, - 'F9': 0x00100000809, - 'F10': 0x0010000080a, - 'F11': 0x0010000080b, - 'F12': 0x0010000080c, - 'F13': 0x0010000080d, - 'F14': 0x0010000080e, - 'F15': 0x0010000080f, - 'F16': 0x00100000810, - 'F17': 0x00100000811, - 'F18': 0x00100000812, - 'F19': 0x00100000813, - 'F20': 0x00100000814, - 'F21': 0x00100000815, - 'F22': 0x00100000816, - 'F23': 0x00100000817, - 'F24': 0x00100000818, - 'Soft 1': 0x00100000901, - 'Soft 2': 0x00100000902, - 'Soft 3': 0x00100000903, - 'Soft 4': 0x00100000904, - 'Soft 5': 0x00100000905, - 'Soft 6': 0x00100000906, - 'Soft 7': 0x00100000907, - 'Soft 8': 0x00100000908, - 'Close': 0x00100000a01, - 'Mail Forward': 0x00100000a02, - 'Mail Reply': 0x00100000a03, - 'Mail Send': 0x00100000a04, - 'Media Play Pause': 0x00100000a05, - 'Media Stop': 0x00100000a07, - 'Media Track Next': 0x00100000a08, - 'Media Track Previous': 0x00100000a09, - 'New': 0x00100000a0a, - 'Open': 0x00100000a0b, - 'Print': 0x00100000a0c, - 'Save': 0x00100000a0d, - 'Spell Check': 0x00100000a0e, - 'Audio Volume Down': 0x00100000a0f, - 'Audio Volume Up': 0x00100000a10, - 'Audio Volume Mute': 0x00100000a11, - 'Launch Application 2': 0x00100000b01, - 'Launch Calendar': 0x00100000b02, - 'Launch Mail': 0x00100000b03, - 'Launch Media Player': 0x00100000b04, - 'Launch Music Player': 0x00100000b05, - 'Launch Application 1': 0x00100000b06, - 'Launch Screen Saver': 0x00100000b07, - 'Launch Spreadsheet': 0x00100000b08, - 'Launch Web Browser': 0x00100000b09, - 'Launch Web Cam': 0x00100000b0a, - 'Launch Word Processor': 0x00100000b0b, - 'Launch Contacts': 0x00100000b0c, - 'Launch Phone': 0x00100000b0d, - 'Launch Assistant': 0x00100000b0e, - 'Launch Control Panel': 0x00100000b0f, - 'Browser Back': 0x00100000c01, - 'Browser Favorites': 0x00100000c02, - 'Browser Forward': 0x00100000c03, - 'Browser Home': 0x00100000c04, - 'Browser Refresh': 0x00100000c05, - 'Browser Search': 0x00100000c06, - 'Browser Stop': 0x00100000c07, - 'Audio Balance Left': 0x00100000d01, - 'Audio Balance Right': 0x00100000d02, - 'Audio Bass Boost Down': 0x00100000d03, - 'Audio Bass Boost Up': 0x00100000d04, - 'Audio Fader Front': 0x00100000d05, - 'Audio Fader Rear': 0x00100000d06, - 'Audio Surround Mode Next': 0x00100000d07, - 'AVR Input': 0x00100000d08, - 'AVR Power': 0x00100000d09, - 'Channel Down': 0x00100000d0a, - 'Channel Up': 0x00100000d0b, - 'Color F0 Red': 0x00100000d0c, - 'Color F1 Green': 0x00100000d0d, - 'Color F2 Yellow': 0x00100000d0e, - 'Color F3 Blue': 0x00100000d0f, - 'Color F4 Grey': 0x00100000d10, - 'Color F5 Brown': 0x00100000d11, - 'Closed Caption Toggle': 0x00100000d12, - 'Dimmer': 0x00100000d13, - 'Display Swap': 0x00100000d14, - 'Exit': 0x00100000d15, - 'Favorite Clear 0': 0x00100000d16, - 'Favorite Clear 1': 0x00100000d17, - 'Favorite Clear 2': 0x00100000d18, - 'Favorite Clear 3': 0x00100000d19, - 'Favorite Recall 0': 0x00100000d1a, - 'Favorite Recall 1': 0x00100000d1b, - 'Favorite Recall 2': 0x00100000d1c, - 'Favorite Recall 3': 0x00100000d1d, - 'Favorite Store 0': 0x00100000d1e, - 'Favorite Store 1': 0x00100000d1f, - 'Favorite Store 2': 0x00100000d20, - 'Favorite Store 3': 0x00100000d21, - 'Guide': 0x00100000d22, - 'Guide Next Day': 0x00100000d23, - 'Guide Previous Day': 0x00100000d24, - 'Info': 0x00100000d25, - 'Instant Replay': 0x00100000d26, - 'Link': 0x00100000d27, - 'List Program': 0x00100000d28, - 'Live Content': 0x00100000d29, - 'Lock': 0x00100000d2a, - 'Media Apps': 0x00100000d2b, - 'Media Fast Forward': 0x00100000d2c, - 'Media Last': 0x00100000d2d, - 'Media Pause': 0x00100000d2e, - 'Media Play': 0x00100000d2f, - 'Media Record': 0x00100000d30, - 'Media Rewind': 0x00100000d31, - 'Media Skip': 0x00100000d32, - 'Next Favorite Channel': 0x00100000d33, - 'Next User Profile': 0x00100000d34, - 'On Demand': 0x00100000d35, - 'P In P Down': 0x00100000d36, - 'P In P Move': 0x00100000d37, - 'P In P Toggle': 0x00100000d38, - 'P In P Up': 0x00100000d39, - 'Play Speed Down': 0x00100000d3a, - 'Play Speed Reset': 0x00100000d3b, - 'Play Speed Up': 0x00100000d3c, - 'Random Toggle': 0x00100000d3d, - 'Rc Low Battery': 0x00100000d3e, - 'Record Speed Next': 0x00100000d3f, - 'Rf Bypass': 0x00100000d40, - 'Scan Channels Toggle': 0x00100000d41, - 'Screen Mode Next': 0x00100000d42, - 'Settings': 0x00100000d43, - 'Split Screen Toggle': 0x00100000d44, - 'STB Input': 0x00100000d45, - 'STB Power': 0x00100000d46, - 'Subtitle': 0x00100000d47, - 'Teletext': 0x00100000d48, - 'TV': 0x00100000d49, - 'TV Input': 0x00100000d4a, - 'TV Power': 0x00100000d4b, - 'Video Mode Next': 0x00100000d4c, - 'Wink': 0x00100000d4d, - 'Zoom Toggle': 0x00100000d4e, - 'DVR': 0x00100000d4f, - 'Media Audio Track': 0x00100000d50, - 'Media Skip Backward': 0x00100000d51, - 'Media Skip Forward': 0x00100000d52, - 'Media Step Backward': 0x00100000d53, - 'Media Step Forward': 0x00100000d54, - 'Media Top Menu': 0x00100000d55, - 'Navigate In': 0x00100000d56, - 'Navigate Next': 0x00100000d57, - 'Navigate Out': 0x00100000d58, - 'Navigate Previous': 0x00100000d59, - 'Pairing': 0x00100000d5a, - 'Media Close': 0x00100000d5b, - 'Audio Bass Boost Toggle': 0x00100000e02, - 'Audio Treble Down': 0x00100000e04, - 'Audio Treble Up': 0x00100000e05, - 'Microphone Toggle': 0x00100000e06, - 'Microphone Volume Down': 0x00100000e07, - 'Microphone Volume Up': 0x00100000e08, - 'Microphone Volume Mute': 0x00100000e09, - 'Speech Correction List': 0x00100000f01, - 'Speech Input Toggle': 0x00100000f02, - 'App Switch': 0x00100001001, - 'Call': 0x00100001002, - 'Camera Focus': 0x00100001003, - 'End Call': 0x00100001004, - 'Go Back': 0x00100001005, - 'Go Home': 0x00100001006, - 'Headset Hook': 0x00100001007, - 'Last Number Redial': 0x00100001008, - 'Notification': 0x00100001009, - 'Manner Mode': 0x0010000100a, - 'Voice Dial': 0x0010000100b, - 'TV 3 D Mode': 0x00100001101, - 'TV Antenna Cable': 0x00100001102, - 'TV Audio Description': 0x00100001103, - 'TV Audio Description Mix Dow': 0x00100001104, - 'TV Audio Description Mix Up': 0x00100001105, - 'TV Contents Menu': 0x00100001106, - 'TV Data Service': 0x00100001107, - 'TV Input Component 1': 0x00100001108, - 'TV Input Component 2': 0x00100001109, - 'TV Input Composite 1': 0x0010000110a, - 'TV Input Composite 2': 0x0010000110b, - 'TV Input HDMI 1': 0x0010000110c, - 'TV Input HDMI 2': 0x0010000110d, - 'TV Input HDMI 3': 0x0010000110e, - 'TV Input HDMI 4': 0x0010000110f, - 'TV Input VGA 1': 0x00100001110, - 'TV Media Context': 0x00100001111, - 'TV Network': 0x00100001112, - 'TV Number Entry': 0x00100001113, - 'TV Radio Service': 0x00100001114, - 'TV Satellite': 0x00100001115, - 'TV Satellite BS': 0x00100001116, - 'TV Satellite CS': 0x00100001117, - 'TV Satellite Toggle': 0x00100001118, - 'TV Terrestrial Analog': 0x00100001119, - 'TV Terrestrial Digital': 0x0010000111a, - 'TV Timer': 0x0010000111b, - 'Key 11': 0x00100001201, - 'Key 12': 0x00100001202, - 'Suspend': 0x00200000000, - 'Resume': 0x00200000001, - 'Sleep': 0x00200000002, - 'Abort': 0x00200000003, - 'Lang 1': 0x00200000010, - 'Lang 2': 0x00200000011, - 'Lang 3': 0x00200000012, - 'Lang 4': 0x00200000013, - 'Lang 5': 0x00200000014, - 'Intl Backslash': 0x00200000020, - 'Intl Ro': 0x00200000021, - 'Intl Yen': 0x00200000022, - 'Control Left': 0x00200000100, - 'Control Right': 0x00200000101, - 'Shift Left': 0x00200000102, - 'Shift Right': 0x00200000103, - 'Alt Left': 0x00200000104, - 'Alt Right': 0x00200000105, - 'Meta Left': 0x00200000106, - 'Meta Right': 0x00200000107, - 'Control': 0x002000001f0, - 'Shift': 0x002000001f2, - 'Alt': 0x002000001f4, - 'Meta': 0x002000001f6, - 'Numpad Enter': 0x0020000020d, - 'Numpad Paren Left': 0x00200000228, - 'Numpad Paren Right': 0x00200000229, - 'Numpad Multiply': 0x0020000022a, - 'Numpad Add': 0x0020000022b, - 'Numpad Comma': 0x0020000022c, - 'Numpad Subtract': 0x0020000022d, - 'Numpad Decimal': 0x0020000022e, - 'Numpad Divide': 0x0020000022f, - 'Numpad 0': 0x00200000230, - 'Numpad 1': 0x00200000231, - 'Numpad 2': 0x00200000232, - 'Numpad 3': 0x00200000233, - 'Numpad 4': 0x00200000234, - 'Numpad 5': 0x00200000235, - 'Numpad 6': 0x00200000236, - 'Numpad 7': 0x00200000237, - 'Numpad 8': 0x00200000238, - 'Numpad 9': 0x00200000239, - 'Numpad Equal': 0x0020000023d, - 'Game Button 1': 0x00200000301, - 'Game Button 2': 0x00200000302, - 'Game Button 3': 0x00200000303, - 'Game Button 4': 0x00200000304, - 'Game Button 5': 0x00200000305, - 'Game Button 6': 0x00200000306, - 'Game Button 7': 0x00200000307, - 'Game Button 8': 0x00200000308, - 'Game Button 9': 0x00200000309, - 'Game Button 10': 0x0020000030a, - 'Game Button 11': 0x0020000030b, - 'Game Button 12': 0x0020000030c, - 'Game Button 13': 0x0020000030d, - 'Game Button 14': 0x0020000030e, - 'Game Button 15': 0x0020000030f, - 'Game Button 16': 0x00200000310, - 'Game Button A': 0x00200000311, - 'Game Button B': 0x00200000312, - 'Game Button C': 0x00200000313, - 'Game Button Left 1': 0x00200000314, - 'Game Button Left 2': 0x00200000315, - 'Game Button Mode': 0x00200000316, - 'Game Button Right 1': 0x00200000317, - 'Game Button Right 2': 0x00200000318, - 'Game Button Select': 0x00200000319, - 'Game Button Start': 0x0020000031a, - 'Game Button Thumb Left': 0x0020000031b, - 'Game Button Thumb Right': 0x0020000031c, - 'Game Button X': 0x0020000031d, - 'Game Button Y': 0x0020000031e, - 'Game Button Z': 0x0020000031f, -}.map((key, value) => MapEntry(key.toLowerCase(), value)); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/keybinding.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/keybinding.dart deleted file mode 100644 index f346514f7c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/keybinding.dart +++ /dev/null @@ -1,153 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/src/service/shortcut_event/key_mapping.dart'; -import 'package:flutter/material.dart'; - -extension KeybindingsExtension on List { - bool containsKeyEvent(RawKeyEvent keyEvent) { - for (final keybinding in this) { - if (keybinding.isMetaPressed == keyEvent.isMetaPressed && - keybinding.isControlPressed == keyEvent.isControlPressed && - keybinding.isAltPressed == keyEvent.isAltPressed && - keybinding.isShiftPressed == keyEvent.isShiftPressed && - keybinding.keyCode == keyEvent.logicalKey.keyId) { - return true; - } - } - return false; - } -} - -class Keybinding { - Keybinding({ - required this.isAltPressed, - required this.isControlPressed, - required this.isMetaPressed, - required this.isShiftPressed, - required this.keyLabel, - }); - - factory Keybinding.parse(String command) { - command = command.toLowerCase().trim(); - - var isAltPressed = false; - var isControlPressed = false; - var isMetaPressed = false; - var isShiftPressed = false; - - var matchedModifier = false; - - do { - matchedModifier = false; - if (RegExp(r'^alt(\+|\-)').hasMatch(command)) { - isAltPressed = true; - command = command.substring(4); // 4 = 'alt '.length - matchedModifier = true; - } - if (RegExp(r'^ctrl(\+|\-)').hasMatch(command)) { - isControlPressed = true; - command = command.substring(5); // 5 = 'ctrl '.length - matchedModifier = true; - } - if (RegExp(r'^shift(\+|\-)').hasMatch(command)) { - isShiftPressed = true; - command = command.substring(6); // 6 = 'shift '.length - matchedModifier = true; - } - if (RegExp(r'^meta(\+|\-)').hasMatch(command)) { - isMetaPressed = true; - command = command.substring(5); // 5 = 'meta '.length - matchedModifier = true; - } - if (RegExp(r'^cmd(\+|\-)').hasMatch(command) || - RegExp(r'^win(\+|\-)').hasMatch(command)) { - isMetaPressed = true; - command = command.substring(4); // 4 = 'win '.length - matchedModifier = true; - } - } while (matchedModifier); - - return Keybinding( - isAltPressed: isAltPressed, - isControlPressed: isControlPressed, - isMetaPressed: isMetaPressed, - isShiftPressed: isShiftPressed, - keyLabel: command, - ); - } - - final bool isAltPressed; - final bool isControlPressed; - final bool isMetaPressed; - final bool isShiftPressed; - final String keyLabel; - - int get keyCode => keyToCodeMapping[keyLabel.toLowerCase()]!; - - Keybinding copyWith({ - bool? isAltPressed, - bool? isControlPressed, - bool? isMetaPressed, - bool? isShiftPressed, - String? keyLabel, - }) { - return Keybinding( - isAltPressed: isAltPressed ?? this.isAltPressed, - isControlPressed: isControlPressed ?? this.isControlPressed, - isMetaPressed: isMetaPressed ?? this.isMetaPressed, - isShiftPressed: isShiftPressed ?? this.isShiftPressed, - keyLabel: keyLabel ?? this.keyLabel, - ); - } - - Map toMap() { - return { - 'isAltPressed': isAltPressed, - 'isControlPressed': isControlPressed, - 'isMetaPressed': isMetaPressed, - 'isShiftPressed': isShiftPressed, - 'keyLabel': keyLabel, - }; - } - - factory Keybinding.fromMap(Map map) { - return Keybinding( - isAltPressed: map['isAltPressed'] ?? false, - isControlPressed: map['isControlPressed'] ?? false, - isMetaPressed: map['isMetaPressed'] ?? false, - isShiftPressed: map['isShiftPressed'] ?? false, - keyLabel: map['keyLabel'] ?? '', - ); - } - - String toJson() => json.encode(toMap()); - - factory Keybinding.fromJson(String source) => - Keybinding.fromMap(json.decode(source)); - - @override - String toString() { - return 'Keybinding(isAltPressed: $isAltPressed, isControlPressed: $isControlPressed, isMetaPressed: $isMetaPressed, isShiftPressed: $isShiftPressed, keyLabel: $keyLabel)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Keybinding && - other.isAltPressed == isAltPressed && - other.isControlPressed == isControlPressed && - other.isMetaPressed == isMetaPressed && - other.isShiftPressed == isShiftPressed && - other.keyCode == keyCode; - } - - @override - int get hashCode { - return isAltPressed.hashCode ^ - isControlPressed.hashCode ^ - isMetaPressed.hashCode ^ - isShiftPressed.hashCode ^ - keyCode; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event.dart deleted file mode 100644 index bba5aff1c3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'dart:io'; - -import 'package:appflowy_editor/src/service/shortcut_event/keybinding.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; -import 'package:flutter/foundation.dart'; - -/// Defines the implementation of shortcut event. -class ShortcutEvent { - ShortcutEvent({ - required this.key, - this.character, - this.command, - required this.handler, - String? windowsCommand, - String? macOSCommand, - String? linuxCommand, - }) { - // character and command cannot be null at the same time - assert( - !(character == null && - command == null && - windowsCommand == null && - macOSCommand == null && - linuxCommand == null), - 'character and command cannot be null at the same time'); - assert( - !(character != null && - (command != null && - windowsCommand != null && - macOSCommand != null && - linuxCommand != null)), - 'character and command cannot be set at the same time'); - - updateCommand( - command: command, - windowsCommand: windowsCommand, - macOSCommand: macOSCommand, - linuxCommand: linuxCommand, - ); - } - - /// The unique key. - /// - /// Usually, uses the description as the key. - final String key; - - /// The string representation for the keyboard keys. - /// - /// The following is the mapping relationship of modify key. - /// ctrl: Ctrl - /// meta: Command in macOS or Control in Windows. - /// alt: Alt - /// shift: Shift - /// cmd: meta - /// win: meta - /// - /// Refer to [keyMapping] for other keys. - /// - /// Uses ',' to split different keyboard key combinations. - /// - /// Like, 'ctrl+c,cmd+c' - /// - String? command; - - String? character; - - final ShortcutEventHandler handler; - - List get keybindings => _keybindings; - List _keybindings = []; - - void updateCommand({ - String? command, - String? windowsCommand, - String? macOSCommand, - String? linuxCommand, - }) { - var matched = false; - if (kIsWeb && command != null && command.isNotEmpty) { - this.command = command; - matched = true; - } else if (Platform.isWindows && - windowsCommand != null && - windowsCommand.isNotEmpty) { - this.command = windowsCommand; - matched = true; - } else if (Platform.isMacOS && - macOSCommand != null && - macOSCommand.isNotEmpty) { - this.command = macOSCommand; - matched = true; - } else if (Platform.isLinux && - linuxCommand != null && - linuxCommand.isNotEmpty) { - this.command = linuxCommand; - matched = true; - } else if (command != null && command.isNotEmpty) { - this.command = command; - matched = true; - } - - if (matched && this.command != null) { - _keybindings = this - .command! - .split(',') - .map((e) => Keybinding.parse(e)) - .toList(growable: false); - } - } - - ShortcutEvent copyWith({ - String? key, - String? command, - ShortcutEventHandler? handler, - }) { - return ShortcutEvent( - key: key ?? this.key, - command: command ?? this.command, - handler: handler ?? this.handler, - ); - } - - @override - String toString() => - 'ShortcutEvent(key: $key, command: $command, handler: $handler)'; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ShortcutEvent && - other.key == key && - other.command == command && - other.handler == handler; - } - - @override - int get hashCode => key.hashCode ^ command.hashCode ^ handler.hashCode; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event_handler.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event_handler.dart deleted file mode 100644 index 07719e045d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/shortcut_event/shortcut_event_handler.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:flutter/material.dart'; - -typedef ShortcutEventHandler = KeyEventResult Function( - EditorState editorState, - RawKeyEvent? event, -); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/toolbar_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/toolbar_service.dart deleted file mode 100644 index 2d63ac8158..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/toolbar_service.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:appflowy_editor/src/flutter/overlay.dart'; -import 'package:flutter/material.dart' hide Overlay, OverlayEntry; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; -import 'package:appflowy_editor/src/extensions/object_extensions.dart'; - -abstract class AppFlowyToolbarService { - /// Show the toolbar widget beside the offset. - void showInOffset( - Offset offset, - Alignment alignment, - LayerLink layerLink, - ); - - /// Hide the toolbar widget. - void hide(); - - /// Trigger the specified handler. - bool triggerHandler(String id); -} - -class FlowyToolbar extends StatefulWidget { - const FlowyToolbar({ - Key? key, - required this.editorState, - required this.child, - }) : super(key: key); - - final EditorState editorState; - final Widget child; - - @override - State createState() => _FlowyToolbarState(); -} - -class _FlowyToolbarState extends State - implements AppFlowyToolbarService { - OverlayEntry? _toolbarOverlay; - final _toolbarWidgetKey = GlobalKey(debugLabel: '_toolbar_widget'); - late final List toolbarItems; - - @override - void initState() { - super.initState(); - - toolbarItems = [...defaultToolbarItems, ...widget.editorState.toolbarItems] - ..sort((a, b) => a.type.compareTo(b.type)); - } - - @override - void showInOffset( - Offset offset, - Alignment alignment, - LayerLink layerLink, - ) { - hide(); - final items = _filterItems(toolbarItems); - if (items.isEmpty) { - return; - } - _toolbarOverlay = OverlayEntry( - builder: (context) => ToolbarWidget( - key: _toolbarWidgetKey, - editorState: widget.editorState, - layerLink: layerLink, - offset: offset, - items: items, - alignment: alignment, - ), - ); - Overlay.of(context)?.insert(_toolbarOverlay!); - } - - @override - void hide() { - _toolbarWidgetKey.currentState?.unwrapOrNull()?.hide(); - _toolbarOverlay?.remove(); - _toolbarOverlay = null; - } - - @override - bool triggerHandler(String id) { - final items = toolbarItems.where((item) => item.id == id); - if (items.length != 1) { - assert(items.length == 1, 'The toolbar item\'s id must be unique'); - return false; - } - items.first.handler?.call(widget.editorState, context); - return true; - } - - @override - Widget build(BuildContext context) { - return Container( - child: widget.child, - ); - } - - @override - void dispose() { - hide(); - - super.dispose(); - } - - // Filter items that should not be displayed, sort according to type, - // and insert dividers between different types. - List _filterItems(List items) { - final filterItems = items - .where((item) => item.validator(widget.editorState)) - .toList(growable: false) - ..sort((a, b) => a.type.compareTo(b.type)); - if (filterItems.isEmpty) { - return []; - } - final List dividedItems = [filterItems.first]; - for (var i = 1; i < filterItems.length; i++) { - if (filterItems[i].type != filterItems[i - 1].type) { - dividedItems.add(ToolbarItem.divider()); - } - dividedItems.add(filterItems[i]); - } - return dividedItems; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/pubspec.yaml b/frontend/appflowy_flutter/packages/appflowy_editor/pubspec.yaml deleted file mode 100644 index c720ac63ea..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/pubspec.yaml +++ /dev/null @@ -1,85 +0,0 @@ -name: appflowy_editor -description: A highly customizable rich-text editor for Flutter -version: 0.0.9 -homepage: https://github.com/AppFlowy-IO/AppFlowy - -platforms: - linux: - macos: - windows: - web: - -environment: - sdk: ">=2.18.0 <3.0.0" - flutter: ">=3.3.0" - -dependencies: - flutter: - sdk: flutter - - rich_clipboard: ^1.0.0 - html: ^0.15.0 - flutter_svg: ^2.0.2 - provider: ^6.0.3 - url_launcher: ^6.1.5 - logging: ^1.0.2 - intl_utils: ^2.7.0 - intl: - flutter_localizations: - sdk: flutter - markdown: ^6.0.1 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.1 - network_image_mock: ^2.1.1 - mockito: ^5.3.2 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - # To add assets to your package, add an assets section, like this: - assets: - - assets/images/toolbar/ - - assets/images/selection_menu/ - - assets/images/image_toolbar/ - - assets/images/ - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages - -flutter_intl: - enabled: true - class_name: AppFlowyEditorLocalizations - main_locale: en - arb_dir: lib/l10n - output_dir: lib/src/l10n - use_deferred_loading: false - localizely: - project_id: b7199c7d-eca0-4025-894d-230cdcafa9aa diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/command/command_extension_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/command/command_extension_test.dart deleted file mode 100644 index 45e5ed24f0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/command/command_extension_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../infra/test_editor.dart'; - -void main() { - group('command_extension.dart', () { - testWidgets('insert a new checkbox after an existing checkbox', - (tester) async { - final editor = tester.editor - ..insertTextNode( - 'Welcome', - ) - ..insertTextNode( - 'to', - ) - ..insertTextNode( - 'Appflowy 😁', - ); - await editor.startTesting(); - final selection = Selection( - start: Position(path: [2], offset: 5), - end: Position(path: [0], offset: 5), - ); - await editor.updateSelection(selection); - final textNodes = editor - .editorState.service.selectionService.currentSelectedNodes - .whereType() - .toList(growable: false); - final texts = editor.editorState.getTextInSelection( - textNodes.normalized, - selection.normalized, - ); - expect(texts, ['me', 'to', 'Appfl']); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/attributes_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/attributes_test.dart deleted file mode 100644 index a7b2349944..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/attributes_test.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('attributes.dart', () { - test('composeAttributes', () { - final base = { - 'a': 1, - 'b': 2, - }; - final other = { - 'b': 3, - 'c': 4, - 'd': null, - }; - expect(composeAttributes(base, other, keepNull: false), { - 'a': 1, - 'b': 3, - 'c': 4, - }); - expect(composeAttributes(base, other, keepNull: true), { - 'a': 1, - 'b': 3, - 'c': 4, - 'd': null, - }); - expect(composeAttributes(null, other, keepNull: false), { - 'b': 3, - 'c': 4, - }); - expect(composeAttributes(base, null, keepNull: false), { - 'a': 1, - 'b': 2, - }); - }); - - test('invertAttributes', () { - final base = { - 'a': 1, - 'b': 2, - }; - final other = { - 'b': 3, - 'c': 4, - 'd': null, - }; - expect(invertAttributes(base, other), { - 'a': 1, - 'b': 2, - 'c': null, - }); - expect(invertAttributes(other, base), { - 'a': null, - 'b': 3, - 'c': 4, - }); - expect(invertAttributes(null, base), { - 'a': null, - 'b': null, - }); - expect(invertAttributes(other, null), { - 'b': 3, - 'c': 4, - }); - }); - test( - "hasAttributes", - () { - final base = { - 'a': 1, - 'b': 2, - }; - final other = { - 'c': 3, - 'd': 4, - }; - - var x = hashAttributes(base); - var y = hashAttributes(base); - // x & y should have same hash code - expect(x == y, true); - - y = hashAttributes(other); - - // x & y should have different hash code - expect(x == y, false); - }, - ); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/document_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/document_test.dart deleted file mode 100644 index 26e3a12c57..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/document_test.dart +++ /dev/null @@ -1,138 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('documemnt.dart', () { - test('insert', () { - final document = Document.empty(); - - expect(document.insert([-1], []), false); - expect(document.insert([100], []), false); - - final node0 = Node(type: '0'); - final node1 = Node(type: '1'); - expect(document.insert([0], [node0, node1]), true); - expect(document.nodeAtPath([0])?.type, '0'); - expect(document.nodeAtPath([1])?.type, '1'); - }); - - test('delete', () { - final document = Document(root: Node(type: 'root')); - - expect(document.delete([-1], 1), false); - expect(document.delete([100], 1), false); - - for (var i = 0; i < 10; i++) { - final node = Node(type: '$i'); - document.insert([i], [node]); - } - - document.delete([0], 10); - expect(document.root.children.isEmpty, true); - }); - - test('update', () { - final node = Node(type: 'example', attributes: {'a': 'a'}); - final document = Document(root: Node(type: 'root')); - document.insert([0], [node]); - - final attributes = { - 'a': 'b', - 'b': 'c', - }; - - expect(document.update([0], attributes), true); - expect(document.nodeAtPath([0])?.attributes, attributes); - - expect(document.update([-1], attributes), false); - }); - - test('updateText', () { - final delta = Delta()..insert('Editor'); - final textNode = TextNode(delta: delta); - final document = Document(root: Node(type: 'root')); - document.insert([0], [textNode]); - document.updateText([0], Delta()..insert('AppFlowy')); - expect((document.nodeAtPath([0]) as TextNode).toPlainText(), - 'AppFlowyEditor'); - }); - - test('serialize', () { - final json = { - 'document': { - 'type': 'editor', - 'children': [ - { - 'type': 'text', - 'delta': [], - } - ], - 'attributes': {'a': 'a'} - } - }; - final document = Document.fromJson(json); - expect(document.toJson(), json); - }); - - test('isEmpty', () { - expect( - true, - Document.fromJson({ - 'document': { - 'type': 'editor', - 'children': [ - { - 'type': 'text', - 'delta': [], - } - ], - } - }).isEmpty, - ); - - expect( - true, - Document.fromJson({ - 'document': { - 'type': 'editor', - 'children': [], - } - }).isEmpty, - ); - - expect( - true, - Document.fromJson({ - 'document': { - 'type': 'editor', - 'children': [ - { - 'type': 'text', - 'delta': [ - {'insert': ''} - ], - } - ], - } - }).isEmpty, - ); - - expect( - false, - Document.fromJson({ - 'document': { - 'type': 'editor', - 'children': [ - { - 'type': 'text', - 'delta': [ - {'insert': 'Welcome to AppFlowy!'} - ], - } - ], - } - }).isEmpty, - ); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/node_iterator_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/node_iterator_test.dart deleted file mode 100644 index 7dd85de18c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/node_iterator_test.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('node_iterator.dart', () { - test('', () { - final root = Node(type: 'root'); - for (var i = 1; i <= 10; i++) { - final node = Node(type: 'node_$i'); - for (var j = 1; j <= i; j++) { - node.insert(Node(type: 'node_${i}_$j')); - } - root.insert(node); - } - final nodes = NodeIterator( - document: Document(root: root), - startNode: root.childAtPath([0])!, - endNode: root.childAtPath([10, 10]), - ); - - for (var i = 1; i <= 10; i++) { - nodes.moveNext(); - expect(nodes.current.type, 'node_$i'); - for (var j = 1; j <= i; j++) { - nodes.moveNext(); - expect(nodes.current.type, 'node_${i}_$j'); - } - } - expect(nodes.moveNext(), false); - }); - - test('toList - when we have at least three level nested nodes (children)', - () { - final root = Node(type: 'root'), - n1 = Node(type: 'node_1'), - n2 = Node(type: 'node_2'); - - root.insert(n1); - root.insert(n2); - n1.insert(Node(type: 'node_1_1')); - n1.insert(Node(type: 'node_1_2')); - n1.childAtIndex(0)?.insert(Node(type: 'node_1_1_1')); - n1.childAtIndex(1)?.insert(Node(type: 'node_1_2_1')); - - final nodes = NodeIterator( - document: Document(root: root), - startNode: root.childAtPath([0])!, - endNode: root.childAtPath([1]), - ).toList(); - - expect(nodes[0].id, n1.id); - expect(nodes[1].id, n1.childAtIndex(0)!.id); - expect(nodes[nodes.length - 1].id, n2.id); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/node_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/node_test.dart deleted file mode 100644 index 853df05671..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/node_test.dart +++ /dev/null @@ -1,250 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('node.dart', () { - test('test node copyWith', () { - final node = Node( - type: 'example', - children: LinkedList(), - attributes: { - 'example': 'example', - }, - ); - expect(node.toJson(), { - 'type': 'example', - 'attributes': { - 'example': 'example', - }, - }); - expect( - node.copyWith().toJson(), - node.toJson(), - ); - - final nodeWithChildren = Node( - type: 'example', - children: LinkedList()..add(node), - attributes: { - 'example': 'example', - }, - ); - expect(nodeWithChildren.toJson(), { - 'type': 'example', - 'attributes': { - 'example': 'example', - }, - 'children': [ - { - 'type': 'example', - 'attributes': { - 'example': 'example', - }, - }, - ], - }); - expect( - nodeWithChildren.copyWith().toJson(), - nodeWithChildren.toJson(), - ); - }); - - test('test textNode copyWith', () { - final textNode = TextNode( - children: LinkedList(), - attributes: { - 'example': 'example', - }, - delta: Delta()..insert('AppFlowy'), - ); - expect(textNode.toJson(), { - 'type': 'text', - 'attributes': { - 'example': 'example', - }, - 'delta': [ - {'insert': 'AppFlowy'}, - ], - }); - expect( - textNode.copyWith().toJson(), - textNode.toJson(), - ); - - final textNodeWithChildren = TextNode( - children: LinkedList()..add(textNode), - attributes: { - 'example': 'example', - }, - delta: Delta()..insert('AppFlowy'), - ); - expect(textNodeWithChildren.toJson(), { - 'type': 'text', - 'attributes': { - 'example': 'example', - }, - 'delta': [ - {'insert': 'AppFlowy'}, - ], - 'children': [ - { - 'type': 'text', - 'attributes': { - 'example': 'example', - }, - 'delta': [ - {'insert': 'AppFlowy'}, - ], - }, - ], - }); - expect( - textNodeWithChildren.copyWith().toJson(), - textNodeWithChildren.toJson(), - ); - }); - - test('test node path', () { - Node previous = Node( - type: 'example', - attributes: {}, - children: LinkedList(), - ); - const len = 10; - for (var i = 0; i < len; i++) { - final node = Node( - type: 'example_$i', - attributes: {}, - children: LinkedList(), - ); - previous.children.add(node..parent = previous); - previous = node; - } - expect(previous.path, List.filled(len, 0)); - }); - - test('test copy with', () { - final child = Node( - type: 'child', - attributes: {}, - children: LinkedList(), - ); - final base = Node( - type: 'base', - attributes: {}, - children: LinkedList()..add(child), - ); - final node = base.copyWith( - type: 'node', - ); - expect(identical(node.attributes, base.attributes), false); - expect(identical(node.children, base.children), false); - expect(identical(node.children.first, base.children.first), false); - }); - - test('test insert', () { - final base = Node( - type: 'base', - ); - - // insert at the front when node's children is empty - final childA = Node( - type: 'child', - ); - base.insert(childA); - expect( - identical(base.childAtIndex(0), childA), - true, - ); - - // insert at the front - final childB = Node( - type: 'child', - ); - base.insert(childB, index: -1); - expect( - identical(base.childAtIndex(0), childB), - true, - ); - - // insert at the last - final childC = Node( - type: 'child', - ); - base.insert(childC, index: 1000); - expect( - identical(base.childAtIndex(base.children.length - 1), childC), - true, - ); - - // insert at the last - final childD = Node( - type: 'child', - ); - base.insert(childD); - expect( - identical(base.childAtIndex(base.children.length - 1), childD), - true, - ); - - // insert at the second - final childE = Node( - type: 'child', - ); - base.insert(childE, index: 1); - expect( - identical(base.childAtIndex(1), childE), - true, - ); - }); - - test('test fromJson', () { - final node = Node.fromJson({ - 'type': 'text', - 'delta': [ - {'insert': 'example'}, - ], - 'children': [ - { - 'type': 'example', - 'attributes': { - 'example': 'example', - }, - }, - ], - }); - expect(node.type, 'text'); - expect(node is TextNode, true); - expect((node as TextNode).delta.toPlainText(), 'example'); - expect(node.attributes, {}); - expect(node.children.length, 1); - expect(node.children.first.type, 'example'); - expect(node.children.first.attributes, {'example': 'example'}); - }); - - test('test toPlainText', () { - final textNode = TextNode.empty()..delta = (Delta()..insert('AppFlowy')); - expect(textNode.toPlainText(), 'AppFlowy'); - }); - test('test node id', () { - final nodeA = Node( - type: 'example', - children: LinkedList(), - attributes: {}, - ); - final nodeAId = nodeA.id; - expect(nodeAId, 'example'); - final nodeB = Node( - type: 'example', - children: LinkedList(), - attributes: { - 'subtype': 'exampleSubtype', - }, - ); - final nodeBId = nodeB.id; - expect(nodeBId, 'example/exampleSubtype'); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/path_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/path_test.dart deleted file mode 100644 index fa2725db97..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/path_test.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('path.dart', () { - test('test path equality', () { - var p1 = [0, 0]; - var p2 = [0]; - - expect(p1 > p2, true); - expect(p1 >= p2, true); - expect(p1 < p2, false); - expect(p1 <= p2, false); - - p1 = [1, 1, 2]; - p2 = [1, 1, 3]; - - expect(p2 > p1, true); - expect(p2 >= p1, true); - expect(p2 < p1, false); - expect(p2 <= p1, false); - - p1 = [2, 0, 1]; - p2 = [2, 0, 1]; - - expect(p2 > p1, false); - expect(p1 > p2, false); - expect(p2 >= p1, true); - expect(p2 <= p1, true); - expect(p1.equals(p2), true); - }); - test( - "test path next, previous and parent getters", - () { - var p1 = [0, 0]; - var p2 = [0, 1]; - - expect(p1.next.equals(p2), true); - expect(p1.previous.equals(p2), false); - expect(p1.parent.equals(p2), false); - - p1 = [0, 1, 0]; - p2 = [0, 1, 1]; - - expect(p2.next.equals(p1), false); - expect(p2.previous.equals(p1), true); - expect(p2.parent.equals(p1), false); - - p1 = [0, 1, 1]; - p2 = [0, 1, 1]; - - expect(p1.next.equals(p2), false); - expect(p1.previous.equals(p2), false); - expect(p1.parent.equals(p2), false); - - p1 = []; - p2 = []; - - expect(p1.next.equals(p2), true); - expect(p2.previous.equals(p1), true); - expect(p1.parent.equals(p2), true); - - p1 = [1, 0, 2]; - p2 = [1, 0]; - - expect(p1.parent.equals(p2), true); - expect(p2.parent.equals(p1), false); - }, - ); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/text_delta_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/text_delta_test.dart deleted file mode 100644 index c0c3946636..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/document/text_delta_test.dart +++ /dev/null @@ -1,332 +0,0 @@ -import 'package:appflowy_editor/src/core/document/attributes.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/src/core/document/text_delta.dart'; - -void main() { - group('text_delta.dart', () { - group('compose', () { - test('test delta', () { - final delta = Delta(operations: [ - TextInsert('Gandalf', attributes: { - 'bold': true, - }), - TextInsert(' the '), - TextInsert('Grey', attributes: { - 'color': '#ccc', - }) - ]); - - final death = Delta() - ..retain(12) - ..insert("White", attributes: { - 'color': '#fff', - }) - ..delete(4); - - final restores = delta.compose(death); - expect(restores.toList(), [ - TextInsert('Gandalf', attributes: {'bold': true}), - TextInsert(' the '), - TextInsert('White', attributes: {'color': '#fff'}), - ]); - }); - test('compose()', () { - final a = Delta()..insert('A'); - final b = Delta()..insert('B'); - final expected = Delta() - ..insert('B') - ..insert('A'); - expect(a.compose(b), expected); - }); - test('insert + retain', () { - final a = Delta()..insert('A'); - final b = Delta() - ..retain(1, attributes: { - 'bold': true, - 'color': 'red', - }); - final expected = Delta() - ..insert('A', attributes: { - 'bold': true, - 'color': 'red', - }); - expect(a.compose(b), expected); - }); - test('insert + delete', () { - final a = Delta()..insert('A'); - final b = Delta()..delete(1); - final expected = Delta(); - expect(a.compose(b), expected); - }); - test('delete + insert', () { - final a = Delta()..delete(1); - final b = Delta()..insert('B'); - final expected = Delta() - ..insert('B') - ..delete(1); - expect(a.compose(b), expected); - }); - test('delete + retain', () { - final a = Delta()..delete(1); - final b = Delta() - ..retain(1, attributes: { - 'bold': true, - 'color': 'red', - }); - final expected = Delta() - ..delete(1) - ..retain(1, attributes: { - 'bold': true, - 'color': 'red', - }); - expect(a.compose(b), expected); - }); - test('delete + delete', () { - final a = Delta()..delete(1); - final b = Delta()..delete(1); - final expected = Delta()..delete(2); - expect(a.compose(b), expected); - }); - test('retain + insert', () { - final a = Delta()..retain(1, attributes: {'color': 'blue'}); - final b = Delta()..insert('B'); - final expected = Delta() - ..insert('B') - ..retain(1, attributes: { - 'color': 'blue', - }); - expect(a.compose(b), expected); - }); - test('retain + retain', () { - final a = Delta() - ..retain(1, attributes: { - 'color': 'blue', - }); - final b = Delta() - ..retain(1, attributes: { - 'bold': true, - 'color': 'red', - }); - final expected = Delta() - ..retain(1, attributes: { - 'bold': true, - 'color': 'red', - }); - expect(a.compose(b), expected); - }); - test('retain + delete', () { - final a = Delta() - ..retain(1, attributes: { - 'color': 'blue', - }); - final b = Delta()..delete(1); - final expected = Delta()..delete(1); - expect(a.compose(b), expected); - }); - test('insert in middle of text', () { - final a = Delta()..insert('Hello'); - final b = Delta() - ..retain(3) - ..insert('X'); - final expected = Delta()..insert('HelXlo'); - expect(a.compose(b), expected); - }); - test('insert and delete ordering', () { - final a = Delta()..insert('Hello'); - final b = Delta()..insert('Hello'); - final insertFirst = Delta() - ..retain(3) - ..insert('X') - ..delete(1); - final deleteFirst = Delta() - ..retain(3) - ..delete(1) - ..insert('X'); - final expected = Delta()..insert('HelXo'); - expect(a.compose(insertFirst), expected); - expect(b.compose(deleteFirst), expected); - }); - test('delete entire text', () { - final a = Delta() - ..retain(4) - ..insert('Hello'); - final b = Delta()..delete(9); - final expected = Delta()..delete(4); - expect(a.compose(b), expected); - }); - test('retain more than length of text', () { - final a = Delta()..insert('Hello'); - final b = Delta()..retain(10); - final expected = Delta()..insert('Hello'); - expect(a.compose(b), expected); - }); - test('retain start optimization', () { - final a = Delta() - ..insert('A', attributes: {'bold': true}) - ..insert('B') - ..insert('C', attributes: {'bold': true}) - ..delete(1); - final b = Delta() - ..retain(3) - ..insert('D'); - final expected = Delta() - ..insert('A', attributes: {'bold': true}) - ..insert('B') - ..insert('C', attributes: {'bold': true}) - ..insert('D') - ..delete(1); - expect(a.compose(b), expected); - }); - test('retain end optimization', () { - final a = Delta() - ..insert('A', attributes: {'bold': true}) - ..insert('B') - ..insert('C', attributes: {'bold': true}); - final b = Delta()..delete(1); - final expected = Delta() - ..insert('B') - ..insert('C', attributes: {'bold': true}); - expect(a.compose(b), expected); - }); - test('retain end optimization join', () { - final a = Delta() - ..insert('A', attributes: {'bold': true}) - ..insert('B') - ..insert('C', attributes: {'bold': true}) - ..insert('D') - ..insert('E', attributes: {'bold': true}) - ..insert('F'); - final b = Delta() - ..retain(1) - ..delete(1); - final expected = Delta() - ..insert('AC', attributes: {'bold': true}) - ..insert('D') - ..insert('E', attributes: {'bold': true}) - ..insert('F'); - expect(a.compose(b), expected); - }); - }); - group('invert', () { - test('insert', () { - final delta = Delta() - ..retain(2) - ..insert('A'); - final base = Delta()..insert('12346'); - final expected = Delta() - ..retain(2) - ..delete(1); - final inverted = delta.invert(base); - expect(expected, inverted); - expect(base.compose(delta).compose(inverted), base); - }); - test('delete', () { - final delta = Delta() - ..retain(2) - ..delete(3); - final base = Delta()..insert('123456'); - final expected = Delta() - ..retain(2) - ..insert('345'); - final inverted = delta.invert(base); - expect(expected, inverted); - expect(base.compose(delta).compose(inverted), base); - }); - test('retain', () { - final delta = Delta() - ..retain(2) - ..retain(3, attributes: {'bold': true}); - final base = Delta()..insert('123456'); - final expected = Delta() - ..retain(2) - ..retain(3, attributes: {'bold': null}); - final inverted = delta.invert(base); - expect(expected, inverted); - final t = base.compose(delta).compose(inverted); - expect(t, base); - }); - }); - group('json', () { - test('toJson()', () { - final delta = Delta() - ..retain(2) - ..insert('A') - ..delete(3); - expect(delta.toJson(), [ - {'retain': 2}, - {'insert': 'A'}, - {'delete': 3} - ]); - }); - test('attributes', () { - final delta = Delta() - ..retain(2, attributes: {'bold': true}) - ..insert('A', attributes: {'italic': true}); - expect(delta.toJson(), [ - { - 'retain': 2, - 'attributes': {'bold': true}, - }, - { - 'insert': 'A', - 'attributes': {'italic': true}, - }, - ]); - }); - test('fromJson()', () { - final delta = Delta.fromJson([ - {'retain': 2}, - {'insert': 'A'}, - {'delete': 3}, - ]); - final expected = Delta() - ..retain(2) - ..insert('A') - ..delete(3); - expect(delta, expected); - }); - }); - group('runes', () { - test("stringIndexes", () { - final indexes = stringIndexes('😊'); - expect(indexes[0], 0); - expect(indexes[1], 0); - }); - test("next rune 1", () { - final delta = Delta()..insert('😊'); - expect(delta.nextRunePosition(0), 2); - }); - test("next rune 2", () { - final delta = Delta()..insert('😊a'); - expect(delta.nextRunePosition(0), 2); - }); - test("next rune 3", () { - final delta = Delta()..insert('😊陈'); - expect(delta.nextRunePosition(2), 3); - }); - test("prev rune 1", () { - final delta = Delta()..insert('😊陈'); - expect(delta.prevRunePosition(2), 0); - }); - test("prev rune 2", () { - final delta = Delta()..insert('😊'); - expect(delta.prevRunePosition(2), 0); - }); - test("prev rune 3", () { - final delta = Delta()..insert('😊'); - expect(delta.prevRunePosition(0), -1); - }); - }); - group("attributes", () { - test("compose", () { - final attrs = - composeAttributes({'a': null}, {'b': null}, keepNull: true); - expect(attrs != null, true); - expect(attrs?.containsKey("a"), true); - expect(attrs?.containsKey("b"), true); - expect(attrs?["a"], null); - expect(attrs?["b"], null); - }); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/location/position_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/location/position_test.dart deleted file mode 100644 index ded398e968..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/location/position_test.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('position.dart', () { - test('test position equality', () { - final positionA = Position(path: [0, 1, 2], offset: 3); - final positionB = Position(path: [0, 1, 2], offset: 3); - expect(positionA, positionB); - - final positionC = positionA.copyWith(offset: 4); - final positionD = positionB.copyWith(path: [1, 2, 3]); - expect(positionC.offset, 4); - expect(positionD.path, [1, 2, 3]); - - expect(positionA.toJson(), { - 'path': [0, 1, 2], - 'offset': 3, - }); - expect(positionC.toJson(), { - 'path': [0, 1, 2], - 'offset': 4, - }); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/location/selection_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/location/selection_test.dart deleted file mode 100644 index 4361ffcf67..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/location/selection_test.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('selection.dart', () { - test('test selection equality', () { - final position = Position(path: [0, 1, 2], offset: 3); - final selectionA = Selection(start: position, end: position); - final selectionB = Selection.collapsed(position); - expect(selectionA, selectionB); - expect(selectionA.hashCode, selectionB.hashCode); - - final newPosition = Position(path: [1, 2, 3], offset: 4); - - final selectionC = selectionA.copyWith(start: newPosition); - expect(selectionC.start, newPosition); - expect(selectionC.end, position); - expect(selectionC.isCollapsed, false); - - final selectionD = selectionA.copyWith(end: newPosition); - expect(selectionD.start, position); - expect(selectionD.end, newPosition); - expect(selectionD.isCollapsed, false); - - final selectionE = Selection.single(path: [0, 1, 2], startOffset: 3); - expect(selectionE, selectionA); - expect(selectionE.isSingle, true); - expect(selectionE.isCollapsed, true); - }); - - test('test selection direction', () { - final start = Position(path: [0, 1, 2], offset: 3); - final end = Position(path: [1, 2, 3], offset: 3); - final backwardSelection = Selection(start: start, end: end); - expect(backwardSelection.isBackward, true); - final forwardSelection = Selection(start: end, end: start); - expect(forwardSelection.isForward, true); - - expect(backwardSelection.reversed, forwardSelection); - expect(forwardSelection.normalized, backwardSelection); - - expect(backwardSelection.startIndex, 3); - expect(backwardSelection.endIndex, 3); - }); - - test('test selection collapsed', () { - final start = Position(path: [0, 1, 2], offset: 3); - final end = Position(path: [1, 2, 3], offset: 3); - final selection = Selection(start: start, end: end); - final collapsedAtStart = selection.collapse(atStart: true); - expect(collapsedAtStart.isCollapsed, true); - expect(collapsedAtStart.start, start); - expect(collapsedAtStart.end, start); - - final collapsedAtEnd = selection.collapse(atStart: false); - expect(collapsedAtEnd.isCollapsed, true); - expect(collapsedAtEnd.start, end); - expect(collapsedAtEnd.end, end); - }); - - test('test selection toJson', () { - final start = Position(path: [0, 1, 2], offset: 3); - final end = Position(path: [1, 2, 3], offset: 3); - final selection = Selection(start: start, end: end); - expect(selection.toJson(), { - 'start': { - 'path': [0, 1, 2], - 'offset': 3 - }, - 'end': { - 'path': [1, 2, 3], - 'offset': 3 - } - }); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/operation_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/operation_test.dart deleted file mode 100644 index d52ba43221..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/operation_test.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('operation.dart', () { - test('test insert operation', () { - final node = Node(type: 'example'); - final op = InsertOperation([0], [node]); - final json = op.toJson(); - expect(json, { - 'op': 'insert', - 'path': [0], - 'nodes': [ - { - 'type': 'example', - } - ] - }); - expect(InsertOperation.fromJson(json), op); - expect(op.invert().invert(), op); - expect(op.copyWith(), op); - }); - - test('test update operation', () { - final op = UpdateOperation([0], {'a': 1}, {'a': 0}); - final json = op.toJson(); - expect(json, { - 'op': 'update', - 'path': [0], - 'attributes': {'a': 1}, - 'oldAttributes': {'a': 0} - }); - expect(UpdateOperation.fromJson(json), op); - expect(op.invert().invert(), op); - expect(op.copyWith(), op); - }); - - test('test delete operation', () { - final node = Node(type: 'example'); - final op = DeleteOperation([0], [node]); - final json = op.toJson(); - expect(json, { - 'op': 'delete', - 'path': [0], - 'nodes': [ - { - 'type': 'example', - } - ] - }); - expect(DeleteOperation.fromJson(json), op); - expect(op.invert().invert(), op); - expect(op.copyWith(), op); - }); - - test('test update text operation', () { - final app = Delta()..insert('App'); - final appflowy = Delta() - ..retain(3) - ..insert('Flowy'); - final op = UpdateTextOperation([0], app, appflowy.invert(app)); - final json = op.toJson(); - expect(json, { - 'op': 'update_text', - 'path': [0], - 'delta': [ - {'insert': 'App'} - ], - 'inverted': [ - {'retain': 3}, - {'delete': 5} - ] - }); - expect(UpdateTextOperation.fromJson(json), op); - expect(op.invert().invert(), op); - expect(op.copyWith(), op); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/transaction_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/transaction_test.dart deleted file mode 100644 index bfb9fa2b3a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/transaction_test.dart +++ /dev/null @@ -1,206 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -Document createEmptyDocument() { - return Document( - root: Node( - type: 'editor', - ), - ); -} - -void main() async { - group('transaction.dart', () { - testWidgets('test replaceTexts, textNodes.length == texts.length', - (tester) async { - TestWidgetsFlutterBinding.ensureInitialized(); - - final editor = tester.editor - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789'); - await editor.startTesting(); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 4); - - final selection = Selection( - start: Position(path: [0], offset: 4), - end: Position(path: [3], offset: 4), - ); - final transaction = editor.editorState.transaction; - var textNodes = [0, 1, 2, 3] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - final texts = ['ABC', 'ABC', 'ABC', 'ABC']; - transaction.replaceTexts(textNodes, selection, texts); - editor.editorState.apply(transaction); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 4); - textNodes = [0, 1, 2, 3] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - expect(textNodes[0].toPlainText(), '0123ABC'); - expect(textNodes[1].toPlainText(), 'ABC'); - expect(textNodes[2].toPlainText(), 'ABC'); - expect(textNodes[3].toPlainText(), 'ABC456789'); - }); - - testWidgets('test replaceTexts, textNodes.length > texts.length', - (tester) async { - TestWidgetsFlutterBinding.ensureInitialized(); - - final editor = tester.editor - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789'); - await editor.startTesting(); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 5); - - final selection = Selection( - start: Position(path: [0], offset: 4), - end: Position(path: [4], offset: 4), - ); - final transaction = editor.editorState.transaction; - var textNodes = [0, 1, 2, 3, 4] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - final texts = ['ABC', 'ABC', 'ABC', 'ABC']; - transaction.replaceTexts(textNodes, selection, texts); - editor.editorState.apply(transaction); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 4); - textNodes = [0, 1, 2, 3] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - expect(textNodes[0].toPlainText(), '0123ABC'); - expect(textNodes[1].toPlainText(), 'ABC'); - expect(textNodes[2].toPlainText(), 'ABC'); - expect(textNodes[3].toPlainText(), 'ABC456789'); - }); - - testWidgets('test replaceTexts, textNodes.length >> texts.length', - (tester) async { - TestWidgetsFlutterBinding.ensureInitialized(); - - final editor = tester.editor - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789'); - await editor.startTesting(); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 5); - - final selection = Selection( - start: Position(path: [0], offset: 4), - end: Position(path: [4], offset: 4), - ); - final transaction = editor.editorState.transaction; - var textNodes = [0, 1, 2, 3, 4] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - final texts = ['ABC']; - transaction.replaceTexts(textNodes, selection, texts); - editor.editorState.apply(transaction); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 1); - textNodes = [0] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - expect(textNodes[0].toPlainText(), '0123ABC456789'); - }); - - testWidgets('test replaceTexts, textNodes.length < texts.length', - (tester) async { - TestWidgetsFlutterBinding.ensureInitialized(); - - final editor = tester.editor - ..insertTextNode('0123456789') - ..insertTextNode('0123456789') - ..insertTextNode('0123456789'); - await editor.startTesting(); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 3); - - final selection = Selection( - start: Position(path: [0], offset: 4), - end: Position(path: [2], offset: 4), - ); - final transaction = editor.editorState.transaction; - var textNodes = [0, 1, 2] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - final texts = ['ABC', 'ABC', 'ABC', 'ABC']; - transaction.replaceTexts(textNodes, selection, texts); - editor.editorState.apply(transaction); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 4); - textNodes = [0, 1, 2, 3] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - expect(textNodes[0].toPlainText(), '0123ABC'); - expect(textNodes[1].toPlainText(), 'ABC'); - expect(textNodes[2].toPlainText(), 'ABC'); - expect(textNodes[3].toPlainText(), 'ABC456789'); - }); - - testWidgets('test replaceTexts, textNodes.length << texts.length', - (tester) async { - TestWidgetsFlutterBinding.ensureInitialized(); - - final editor = tester.editor..insertTextNode('Welcome to AppFlowy!'); - await editor.startTesting(); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 1); - - // select 'to' - final selection = Selection( - start: Position(path: [0], offset: 8), - end: Position(path: [0], offset: 10), - ); - final transaction = editor.editorState.transaction; - var textNodes = [0] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - final texts = ['ABC1', 'ABC2', 'ABC3', 'ABC4', 'ABC5']; - transaction.replaceTexts(textNodes, selection, texts); - editor.editorState.apply(transaction); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 5); - textNodes = [0, 1, 2, 3, 4] - .map((e) => editor.nodeAtPath([e])!) - .whereType() - .toList(growable: false); - expect(textNodes[0].toPlainText(), 'Welcome ABC1'); - expect(textNodes[1].toPlainText(), 'ABC2'); - expect(textNodes[2].toPlainText(), 'ABC3'); - expect(textNodes[3].toPlainText(), 'ABC4'); - expect(textNodes[4].toPlainText(), 'ABC5 AppFlowy!'); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/attributes_extension_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/attributes_extension_test.dart deleted file mode 100644 index 427e53f32a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/attributes_extension_test.dart +++ /dev/null @@ -1,201 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('NodeAttributesExtensions::', () { - test('heading', () { - final Attributes attribute = { - 'subtype': 'heading', - 'heading': 'AppFlowy', - }; - expect(attribute.heading, 'AppFlowy'); - }); - - test('heading - text is not String return null', () { - final Attributes attribute = { - 'subtype': 'heading', - 'heading': 123, - }; - expect(attribute.heading, null); - }); - - test('heading - subtype is not "heading" return null', () { - final Attributes attribute = { - 'subtype': 'code', - 'heading': 'Hello World!', - }; - expect(attribute.heading, null); - }); - - test('quote', () { - final Attributes attribute = { - 'quote': 'quote text', - }; - expect(attribute.quote, true); - }); - - test('number - int', () { - final Attributes attribute = { - 'number': 99, - }; - expect(attribute.number, 99); - }); - - test('number - double', () { - final Attributes attribute = { - 'number': 12.34, - }; - expect(attribute.number, 12.34); - }); - - test('number - return null', () { - final Attributes attribute = { - 'code': 12.34, - }; - expect(attribute.number, null); - }); - - test('code', () { - final Attributes attribute = { - 'code': true, - }; - expect(attribute.code, true); - }); - - test('code - return false', () { - final Attributes attribute = { - 'quote': true, - }; - expect(attribute.code, false); - }); - - test('check', () { - final Attributes attribute = { - 'checkbox': true, - }; - expect(attribute.check, true); - }); - - test('check - return false', () { - final Attributes attribute = { - 'quote': true, - }; - expect(attribute.check, false); - }); - }); - - group('DeltaAttributesExtensions::', () { - test('bold', () { - final Attributes attribute = { - 'bold': true, - }; - expect(attribute.bold, true); - }); - - test('bold - return false', () { - final Attributes attribute = { - 'bold': 123, - }; - expect(attribute.bold, false); - }); - - test('italic', () { - final Attributes attribute = { - 'italic': true, - }; - expect(attribute.italic, true); - }); - - test('italic - return false', () { - final Attributes attribute = { - 'italic': 123, - }; - expect(attribute.italic, false); - }); - - test('underline', () { - final Attributes attribute = { - 'underline': true, - }; - expect(attribute.underline, true); - }); - - test('underline - return false', () { - final Attributes attribute = { - 'underline': 123, - }; - expect(attribute.underline, false); - }); - - test('strikethrough', () { - final Attributes attribute = { - 'strikethrough': true, - }; - expect(attribute.strikethrough, true); - }); - - test('strikethrough - return false', () { - final Attributes attribute = { - 'strikethrough': 123, - }; - expect(attribute.strikethrough, false); - }); - - test('color', () { - final Attributes attribute = { - 'color': '0xff212fff', - }; - expect(attribute.color, const Color(0XFF212FFF)); - }); - - test('color - return null', () { - final Attributes attribute = { - 'color': 123, - }; - expect(attribute.color, null); - }); - - test('color - parse failure return white', () { - final Attributes attribute = { - 'color': 'hello123', - }; - expect(attribute.color, const Color(0XFFFFFFFF)); - }); - - test('backgroundColor', () { - final Attributes attribute = { - 'backgroundColor': '0xff678fff', - }; - expect(attribute.backgroundColor, const Color(0XFF678FFF)); - }); - - test('backgroundColor - return null', () { - final Attributes attribute = { - 'backgroundColor': 123, - }; - expect(attribute.backgroundColor, null); - }); - - test('backgroundColor - parse failure return white', () { - final Attributes attribute = { - 'backgroundColor': 'hello123', - }; - expect(attribute.backgroundColor, const Color(0XFFFFFFFF)); - }); - - test('href', () { - final Attributes attribute = { - 'href': '/app/flowy', - }; - expect(attribute.href, '/app/flowy'); - }); - - test('href - return null', () { - final Attributes attribute = { - 'href': 123, - }; - expect(attribute.href, null); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/color_extension_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/color_extension_test.dart deleted file mode 100644 index 929a5c0378..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/color_extension_test.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:appflowy_editor/src/extensions/color_extension.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('ColorExtension::', () { - const white = Color(0XFFFFFFFF); - const black = Color(0XFF000000); - const blue = Color(0XFF000FFF); - const blueRgba = 'rgba(0, 15, 255, 255)'; - test('ToRgbaString', () { - expect(blue.toRgbaString(), 'rgba(0, 15, 255, 255)'); - expect(white.toRgbaString(), 'rgba(255, 255, 255, 255)'); - expect(black.toRgbaString(), 'rgba(0, 0, 0, 255)'); - }); - - test('tryFromRgbaString', () { - final color = ColorExtension.tryFromRgbaString(blueRgba); - expect(color, const Color.fromARGB(255, 0, 15, 255)); - }); - - test('tryFromRgbaString - wrong rgba format return null', () { - const wrongRgba = 'abc(1,2,3,4)'; - final color = ColorExtension.tryFromRgbaString(wrongRgba); - expect(color, null); - }); - - test('tryFromRgbaString - wrong length return null', () { - const wrongRgba = 'rgba(0, 15, 255)'; - final color = ColorExtension.tryFromRgbaString(wrongRgba); - expect(color, null); - }); - - test('tryFromRgbaString - wrong values return null', () { - const wrongRgba = 'rgba(-12, 999, 1234, 619)'; - final color = ColorExtension.tryFromRgbaString(wrongRgba); - expect(color, null); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/node_extension_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/node_extension_test.dart deleted file mode 100644 index 9aa3db6bb2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/node_extension_test.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../infra/test_editor.dart'; -import 'package:mockito/mockito.dart'; - -class MockNode extends Mock implements Node {} - -void main() { - group('node_extension.dart', () { - final selection = Selection( - start: Position(path: [0]), - end: Position(path: [1]), - ); - - test('inSelection', () { - // I use an empty implementation instead of mock, because the mocked - // version throws error trying to access the path. - - final subLinkedList = LinkedList() - ..addAll([ - Node(type: 'type', children: LinkedList(), attributes: {}), - Node(type: 'type', children: LinkedList(), attributes: {}), - Node(type: 'type', children: LinkedList(), attributes: {}), - Node(type: 'type', children: LinkedList(), attributes: {}), - Node(type: 'type', children: LinkedList(), attributes: {}), - ]); - - final linkedList = LinkedList() - ..addAll([ - Node( - type: 'type', - children: subLinkedList, - attributes: {}, - ), - ]); - - final node = Node( - type: 'type', - children: linkedList, - attributes: {}, - ); - final result = node.inSelection(selection); - expect(result, false); - }); - - testWidgets('insert a new checkbox after an existing checkbox', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode( - text, - ) - ..insertTextNode( - text, - ) - ..insertTextNode( - text, - ); - await editor.startTesting(); - final selection = Selection( - start: Position(path: [2], offset: 5), - end: Position(path: [0], offset: 5), - ); - await editor.updateSelection(selection); - final nodes = - editor.editorState.service.selectionService.currentSelectedNodes; - expect( - nodes.map((e) => e.path).toList().toString(), - '[[2], [1], [0]]', - ); - expect( - nodes.normalized.map((e) => e.path).toList().toString(), - '[[0], [1], [2]]', - ); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/object_extension_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/object_extension_test.dart deleted file mode 100644 index 151df9cc31..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/object_extension_test.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/src/extensions/object_extensions.dart'; - -void main() { - group('FlowyObjectExtensions::', () { - test('unwrapOrNull', () { - final result = const TextSpan().unwrapOrNull(); - assert(result is TextSpan); - }); - - test('unwrapOrNull - return null', () { - final result = const TextSpan().unwrapOrNull(); - expect(result, null); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/path_extensions_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/path_extensions_test.dart deleted file mode 100644 index 231be64c0a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/path_extensions_test.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('path_extensions.dart', () { - test('test path equality', () { - var p1 = [0, 0]; - var p2 = [0]; - - expect(p1 > p2, true); - expect(p1 >= p2, true); - expect(p1 < p2, false); - expect(p1 <= p2, false); - - p1 = [1, 1, 2]; - p2 = [1, 1, 3]; - - expect(p2 > p1, true); - expect(p2 >= p1, true); - expect(p2 < p1, false); - expect(p2 <= p1, false); - - p1 = [2, 0, 1]; - p2 = [2, 0, 1]; - - expect(p2 > p1, false); - expect(p1 > p2, false); - expect(p2 >= p1, true); - expect(p2 <= p1, true); - expect(p1.equals(p2), true); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart deleted file mode 100644 index 0d7f1ba125..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('TextNodeExtension::', () { - test('description', () {}); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/text_style_extension_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/text_style_extension_test.dart deleted file mode 100644 index 572178d0f3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/text_style_extension_test.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; - -void main() { - group('TextStyleExtensions::', () { - const style = TextStyle( - color: Colors.blue, - backgroundColor: Colors.white, - fontSize: 14, - height: 100, - wordSpacing: 2, - fontWeight: FontWeight.w700, - ); - - const otherStyle = TextStyle( - color: Colors.red, - backgroundColor: Colors.black, - fontSize: 12, - height: 10, - wordSpacing: 1, - ); - test('combine', () { - final result = style.combine(otherStyle); - expect(result.color, Colors.red); - expect(result.backgroundColor, Colors.black); - expect(result.fontSize, 12); - expect(result.height, 10); - expect(result.wordSpacing, 1); - }); - - test('combine - return this', () { - final result = style.combine(null); - expect(result, style); - }); - - test('combine - return null with inherit', () { - final styleCopy = otherStyle.copyWith(inherit: false); - final result = style.combine(styleCopy); - expect(result, styleCopy); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart deleted file mode 100644 index 81b5b51e37..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - test('safeLaunchUrl without scheme', () async { - const href = null; - final result = await safeLaunchUrl(href); - expect(result, false); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/infra_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/infra_test.dart deleted file mode 100644 index 9ed4a150b2..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/infra_test.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/infra/infra.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('infra.dart', () { - test('find the last text node', () { - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - const text = 'Welcome to Appflowy 😁'; - TextNode textNode() { - return TextNode( - delta: Delta()..insert(text), - ); - } - - final node110 = textNode(); - final node111 = textNode(); - final node11 = textNode() - ..insert(node110) - ..insert(node111); - final node10 = textNode(); - final node1 = textNode() - ..insert(node10) - ..insert(node11); - final node0 = textNode(); - final node = textNode() - ..insert(node0) - ..insert(node1); - - expect(Infra.findLastTextNode(node)?.path, [1, 1, 1]); - expect(Infra.findLastTextNode(node0)?.path, [0]); - expect(Infra.findLastTextNode(node1)?.path, [1, 1, 1]); - expect(Infra.findLastTextNode(node10)?.path, [1, 0]); - expect(Infra.findLastTextNode(node11)?.path, [1, 1, 1]); - - expect(Infra.forwardNearestTextNode(node111)?.path, [1, 1, 0]); - expect(Infra.forwardNearestTextNode(node110)?.path, [1, 1]); - expect(Infra.forwardNearestTextNode(node11)?.path, [1, 0]); - expect(Infra.forwardNearestTextNode(node10)?.path, [1]); - expect(Infra.forwardNearestTextNode(node1)?.path, [0]); - expect(Infra.forwardNearestTextNode(node0)?.path, []); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/log_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/log_test.dart deleted file mode 100644 index f49a32c130..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/log_test.dart +++ /dev/null @@ -1,169 +0,0 @@ -import 'package:appflowy_editor/src/infra/log.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'test_editor.dart'; - -void main() async { - group('log.dart', () { - testWidgets('test LogConfiguration in EditorState', (tester) async { - TestWidgetsFlutterBinding.ensureInitialized(); - - const text = 'Welcome to Appflowy 😁'; - - final List logs = []; - - final editor = tester.editor; - editor.editorState.logConfiguration - ..level = LogLevel.all - ..handler = (message) { - logs.add(message); - }; - - Log.editor.debug(text); - expect(logs.last.contains('DEBUG'), true); - expect(logs.length, 1); - }); - - test('test LogLevel.all', () { - const text = 'Welcome to Appflowy 😁'; - - final List logs = []; - LogConfiguration() - ..level = LogLevel.all - ..handler = (message) { - logs.add(message); - }; - - Log.editor.debug(text); - expect(logs.last.contains('DEBUG'), true); - Log.editor.info(text); - expect(logs.last.contains('INFO'), true); - Log.editor.warn(text); - expect(logs.last.contains('WARN'), true); - Log.editor.error(text); - expect(logs.last.contains('ERROR'), true); - - expect(logs.length, 4); - }); - - test('test LogLevel.off', () { - const text = 'Welcome to Appflowy 😁'; - - final List logs = []; - LogConfiguration() - ..level = LogLevel.off - ..handler = (message) { - logs.add(message); - }; - - Log.editor.debug(text); - Log.editor.info(text); - Log.editor.warn(text); - Log.editor.error(text); - - expect(logs.length, 0); - }); - - test('test LogLevel.error', () { - const text = 'Welcome to Appflowy 😁'; - - final List logs = []; - LogConfiguration() - ..level = LogLevel.error - ..handler = (message) { - logs.add(message); - }; - - Log.editor.debug(text); - Log.editor.info(text); - Log.editor.warn(text); - Log.editor.error(text); - - expect(logs.length, 1); - }); - - test('test LogLevel.warn', () { - const text = 'Welcome to Appflowy 😁'; - - final List logs = []; - LogConfiguration() - ..level = LogLevel.warn - ..handler = (message) { - logs.add(message); - }; - - Log.editor.debug(text); - Log.editor.info(text); - Log.editor.warn(text); - Log.editor.error(text); - - expect(logs.length, 2); - }); - - test('test LogLevel.info', () { - const text = 'Welcome to Appflowy 😁'; - - final List logs = []; - LogConfiguration() - ..level = LogLevel.info - ..handler = (message) { - logs.add(message); - }; - - Log.editor.debug(text); - Log.editor.info(text); - Log.editor.warn(text); - Log.editor.error(text); - - expect(logs.length, 3); - }); - - test('test LogLevel.debug', () { - const text = 'Welcome to Appflowy 😁'; - - final List logs = []; - LogConfiguration() - ..level = LogLevel.debug - ..handler = (message) { - logs.add(message); - }; - - Log.editor.debug(text); - Log.editor.info(text); - Log.editor.warn(text); - Log.editor.error(text); - - expect(logs.length, 4); - }); - - test('test logger', () { - const text = 'Welcome to Appflowy 😁'; - - final List logs = []; - LogConfiguration() - ..level = LogLevel.all - ..handler = (message) { - logs.add(message); - }; - - Log.editor.debug(text); - expect(logs.last.contains('editor'), true); - - Log.selection.debug(text); - expect(logs.last.contains('selection'), true); - - Log.keyboard.debug(text); - expect(logs.last.contains('keyboard'), true); - - Log.input.debug(text); - expect(logs.last.contains('input'), true); - - Log.scroll.debug(text); - expect(logs.last.contains('scroll'), true); - - Log.ui.debug(text); - expect(logs.last.contains('ui'), true); - - expect(logs.length, 6); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_editor.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_editor.dart deleted file mode 100644 index 910e101b63..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_editor.dart +++ /dev/null @@ -1,217 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'test_raw_key_event.dart'; - -class EditorWidgetTester { - EditorWidgetTester({ - required this.tester, - }); - - final WidgetTester tester; - late EditorState _editorState; - - EditorState get editorState => _editorState; - Node get root => _editorState.document.root; - - Document get document => _editorState.document; - int get documentLength => _editorState.document.root.children.length; - Selection? get documentSelection => - _editorState.service.selectionService.currentSelection.value; - - Future startTesting({ - Locale locale = const Locale('en'), - }) async { - final app = MaterialApp( - localizationsDelegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - AppFlowyEditorLocalizations.delegate, - ], - supportedLocales: AppFlowyEditorLocalizations.delegate.supportedLocales, - locale: locale, - home: Scaffold( - body: AppFlowyEditor( - editorState: _editorState, - ), - ), - ); - await tester.pumpWidget(app); - await tester.pump(); - return this; - } - - void initialize() { - _editorState = _createEmptyDocument(); - } - - void insert(T node) { - _editorState.document.root.insert(node); - } - - void insertEmptyTextNode() { - insert(TextNode.empty()); - } - - void insertTextNode(String? text, {Attributes? attributes, Delta? delta}) { - insert( - TextNode( - delta: delta ?? Delta(operations: [TextInsert(text ?? 'Test')]), - attributes: attributes, - ), - ); - } - - void insertImageNode(String src, {String? align, double? width}) { - insert( - Node( - type: 'image', - children: LinkedList(), - attributes: { - 'image_src': src, - 'align': align ?? 'center', - ...width != null ? {'width': width} : {}, - }, - ), - ); - } - - Node? nodeAtPath(Path path) { - return root.childAtPath(path); - } - - Future updateSelection(Selection? selection) async { - if (selection == null) { - _editorState.service.selectionService.clearSelection(); - } else { - _editorState.service.selectionService.updateSelection(selection); - } - await tester.pump(const Duration(milliseconds: 200)); - - expect(_editorState.service.selectionService.currentSelection.value, - selection); - } - - Future insertText(TextNode textNode, String text, int offset, - {Selection? selection}) async { - await apply([ - TextEditingDeltaInsertion( - oldText: textNode.toPlainText(), - textInserted: text, - insertionOffset: offset, - selection: selection != null - ? TextSelection( - baseOffset: selection.start.offset, - extentOffset: selection.end.offset) - : TextSelection.collapsed(offset: offset), - composing: TextRange.empty, - ) - ]); - } - - Future apply(List deltas) async { - _editorState.service.inputService?.apply(deltas); - await tester.pumpAndSettle(); - } - - Future pressLogicKey({ - String? character, - LogicalKeyboardKey? key, - bool isControlPressed = false, - bool isShiftPressed = false, - bool isAltPressed = false, - bool isMetaPressed = false, - }) async { - if (!isControlPressed && - !isShiftPressed && - !isAltPressed && - !isMetaPressed && - key != null) { - await tester.sendKeyDownEvent(key); - } else { - final testRawKeyEventData = TestRawKeyEventData( - logicalKey: key ?? LogicalKeyboardKey.nonConvert, - character: character, - isControlPressed: isControlPressed, - isShiftPressed: isShiftPressed, - isAltPressed: isAltPressed, - isMetaPressed: isMetaPressed, - ).toKeyEvent; - _editorState.service.keyboardService!.onKey(testRawKeyEventData); - } - await tester.pumpAndSettle(); - } - - Node _createEmptyEditorRoot() { - return Node( - type: 'editor', - children: LinkedList(), - attributes: {}, - ); - } - - EditorState _createEmptyDocument() { - return EditorState( - document: Document( - root: _createEmptyEditorRoot(), - ), - ) - ..disableSealTimer = true - ..disableRules = true; - } - - bool runAction(int actionIndex, Node node) { - final builder = editorState.service.renderPluginService.getBuilder(node.id); - if (builder is! ActionProvider) { - return false; - } - - final buildContext = node.key.currentContext; - if (buildContext == null) { - return false; - } - - final context = node is TextNode - ? NodeWidgetContext( - context: buildContext, - node: node, - editorState: editorState, - ) - : NodeWidgetContext( - context: buildContext, - node: node, - editorState: editorState, - ); - - final actions = - builder.actions(context).where((a) => a.onPressed != null).toList(); - if (actionIndex > actions.length) { - return false; - } - - final action = actions[actionIndex]; - action.onPressed!(); - return true; - } -} - -extension TestString on String { - String safeSubString([int start = 0, int? end]) { - end ??= length - 1; - end = end.clamp(start, length - 1); - final sRunes = runes; - return String.fromCharCodes(sRunes, start, end); - } -} - -extension TestEditorExtension on WidgetTester { - EditorWidgetTester get editor => - EditorWidgetTester(tester: this)..initialize(); - EditorState get editorState => editor.editorState; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_raw_key_event.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_raw_key_event.dart deleted file mode 100644 index 2b62a7022a..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/infra/test_raw_key_event.dart +++ /dev/null @@ -1,164 +0,0 @@ -import 'package:flutter/services.dart'; - -class TestRawKeyEvent extends RawKeyDownEvent { - const TestRawKeyEvent({ - required super.data, - required super.character, - this.isControlPressed = false, - this.isShiftPressed = false, - this.isAltPressed = false, - this.isMetaPressed = false, - }); - - @override - final bool isControlPressed; - - @override - final bool isShiftPressed; - - @override - final bool isAltPressed; - - @override - final bool isMetaPressed; -} - -class TestRawKeyEventData extends RawKeyEventData { - const TestRawKeyEventData({ - this.logicalKey = - LogicalKeyboardKey.nonConvert, // use nonConvert as placeholder. - this.character, - this.isControlPressed = false, - this.isShiftPressed = false, - this.isAltPressed = false, - this.isMetaPressed = false, - }); - - @override - final bool isControlPressed; - - @override - final bool isShiftPressed; - - @override - final bool isAltPressed; - - @override - final bool isMetaPressed; - - @override - final LogicalKeyboardKey logicalKey; - - final String? character; - - @override - PhysicalKeyboardKey get physicalKey => logicalKey.toPhysicalKey; - - @override - KeyboardSide? getModifierSide(ModifierKey key) { - throw UnimplementedError(); - } - - @override - bool isModifierPressed( - ModifierKey key, { - KeyboardSide side = KeyboardSide.any, - }) { - throw UnimplementedError(); - } - - @override - String get keyLabel => throw UnimplementedError(); - - RawKeyEvent get toKeyEvent { - return TestRawKeyEvent( - data: this, - character: character, - isAltPressed: isAltPressed, - isControlPressed: isControlPressed, - isMetaPressed: isMetaPressed, - isShiftPressed: isShiftPressed, - ); - } -} - -extension on LogicalKeyboardKey { - PhysicalKeyboardKey get toPhysicalKey { - if (this == LogicalKeyboardKey.enter) { - return PhysicalKeyboardKey.enter; - } - if (this == LogicalKeyboardKey.space) { - return PhysicalKeyboardKey.space; - } - if (this == LogicalKeyboardKey.backspace) { - return PhysicalKeyboardKey.backspace; - } - if (this == LogicalKeyboardKey.delete) { - return PhysicalKeyboardKey.delete; - } - if (this == LogicalKeyboardKey.arrowRight) { - return PhysicalKeyboardKey.arrowRight; - } - if (this == LogicalKeyboardKey.arrowLeft) { - return PhysicalKeyboardKey.arrowLeft; - } - if (this == LogicalKeyboardKey.pageDown) { - return PhysicalKeyboardKey.pageDown; - } - if (this == LogicalKeyboardKey.pageUp) { - return PhysicalKeyboardKey.pageUp; - } - if (this == LogicalKeyboardKey.slash) { - return PhysicalKeyboardKey.slash; - } - if (this == LogicalKeyboardKey.arrowUp) { - return PhysicalKeyboardKey.arrowUp; - } - if (this == LogicalKeyboardKey.arrowDown) { - return PhysicalKeyboardKey.arrowDown; - } - if (this == LogicalKeyboardKey.keyA) { - return PhysicalKeyboardKey.keyA; - } - if (this == LogicalKeyboardKey.keyB) { - return PhysicalKeyboardKey.keyB; - } - if (this == LogicalKeyboardKey.keyC) { - return PhysicalKeyboardKey.keyC; - } - if (this == LogicalKeyboardKey.keyE) { - return PhysicalKeyboardKey.keyE; - } - if (this == LogicalKeyboardKey.keyI) { - return PhysicalKeyboardKey.keyI; - } - if (this == LogicalKeyboardKey.keyK) { - return PhysicalKeyboardKey.keyK; - } - if (this == LogicalKeyboardKey.keyS) { - return PhysicalKeyboardKey.keyS; - } - if (this == LogicalKeyboardKey.keyU) { - return PhysicalKeyboardKey.keyU; - } - if (this == LogicalKeyboardKey.keyH) { - return PhysicalKeyboardKey.keyH; - } - if (this == LogicalKeyboardKey.keyQ) { - return PhysicalKeyboardKey.keyQ; - } - if (this == LogicalKeyboardKey.keyZ) { - return PhysicalKeyboardKey.keyZ; - } - if (this == LogicalKeyboardKey.tilde) { - return PhysicalKeyboardKey.backquote; - } - if (this == LogicalKeyboardKey.digit8) { - return PhysicalKeyboardKey.digit8; - } - if (this == LogicalKeyboardKey.underscore) { - return PhysicalKeyboardKey.minus; - } - throw UnimplementedError(); - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/l10n/l10n_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/l10n/l10n_test.dart deleted file mode 100644 index 2e149d7b4f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/l10n/l10n_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('l10n.dart', () { - for (final locale - in AppFlowyEditorLocalizations.delegate.supportedLocales) { - testWidgets('test localization', (tester) async { - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(locale: locale); - }); - } - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/flowy_editor_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/flowy_editor_test.dart deleted file mode 100644 index 29c38650f3..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/flowy_editor_test.dart +++ /dev/null @@ -1,136 +0,0 @@ -import 'package:appflowy_editor/src/core/document/path.dart'; -import 'package:appflowy_editor/src/core/location/position.dart'; -import 'package:appflowy_editor/src/core/location/selection.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - test('create state tree', () async { - // final String response = await rootBundle.loadString('assets/document.json'); - // final data = Map.from(json.decode(response)); - // final document = Document.fromJson(data); - // expect(document.root.type, 'root'); - // expect(document.root.toJson(), data['document']); - }); - - test('search node by Path in state tree', () async { - // final String response = await rootBundle.loadString('assets/document.json'); - // final data = Map.from(json.decode(response)); - // final document = Document.fromJson(data); - // final checkBoxNode = document.root.childAtPath([1, 0]); - // expect(checkBoxNode != null, true); - // final textType = checkBoxNode!.attributes['text-type']; - // expect(textType != null, true); - }); - - test('search node by Self in state tree', () async { - // final String response = await rootBundle.loadString('assets/document.json'); - // final data = Map.from(json.decode(response)); - // final document = Document.fromJson(data); - // final checkBoxNode = document.root.childAtPath([1, 0]); - // expect(checkBoxNode != null, true); - // final textType = checkBoxNode!.attributes['text-type']; - // expect(textType != null, true); - // final path = checkBoxNode.path; - // expect(pathEquals(path, [1, 0]), true); - }); - - test('insert node in state tree', () async { - // final String response = await rootBundle.loadString('assets/document.json'); - // final data = Map.from(json.decode(response)); - // final document = Document.fromJson(data); - // final insertNode = Node.fromJson({ - // 'type': 'text', - // }); - // bool result = document.insert([1, 1], [insertNode]); - // expect(result, true); - // expect(identical(insertNode, document.nodeAtPath([1, 1])), true); - }); - - test('delete node in state tree', () async { - // final String response = await rootBundle.loadString('assets/document.json'); - // final data = Map.from(json.decode(response)); - // final document = Document.fromJson(data); - // document.delete([1, 1], 1); - // final node = document.nodeAtPath([1, 1]); - // expect(node != null, true); - // expect(node!.attributes['tag'], '**'); - }); - - test('update node in state tree', () async { - // final String response = await rootBundle.loadString('assets/document.json'); - // final data = Map.from(json.decode(response)); - // final document = Document.fromJson(data); - // final test = document.update([1, 1], {'text-type': 'heading1'}); - // expect(test, true); - // final updatedNode = document.nodeAtPath([1, 1]); - // expect(updatedNode != null, true); - // expect(updatedNode!.attributes['text-type'], 'heading1'); - }); - - test('test path utils 1', () { - final path1 = [1]; - final path2 = [1]; - expect(path1.equals(path2), true); - - expect(Object.hashAll(path1), Object.hashAll(path2)); - }); - - test('test path utils 2', () { - final path1 = [1]; - final path2 = [2]; - expect(path1.equals(path2), false); - - expect(Object.hashAll(path1) != Object.hashAll(path2), true); - }); - - test('test position comparator', () { - final pos1 = Position(path: [1], offset: 0); - final pos2 = Position(path: [1], offset: 0); - expect(pos1 == pos2, true); - expect(pos1.hashCode == pos2.hashCode, true); - }); - - test('test position comparator with offset', () { - final pos1 = Position(path: [1, 1, 1, 1, 1], offset: 100); - final pos2 = Position(path: [1, 1, 1, 1, 1], offset: 100); - expect(pos1, pos2); - expect(pos1.hashCode, pos2.hashCode); - }); - - test('test position comparator false', () { - final pos1 = Position(path: [1, 1, 1, 1, 1], offset: 100); - final pos2 = Position(path: [1, 1, 2, 1, 1], offset: 100); - expect(pos1 == pos2, false); - expect(pos1.hashCode == pos2.hashCode, false); - }); - - test('test position comparator with offset false', () { - final pos1 = Position(path: [1, 1, 1, 1, 1], offset: 100); - final pos2 = Position(path: [1, 1, 1, 1, 1], offset: 101); - expect(pos1 == pos2, false); - expect(pos1.hashCode == pos2.hashCode, false); - }); - - test('test selection comparator', () { - final pos = Position(path: [0], offset: 0); - final sel = Selection.collapsed(pos); - expect(sel.start, sel.end); - expect(sel.isCollapsed, true); - }); - - test('test selection collapse', () { - final start = Position(path: [0], offset: 0); - final end = Position(path: [0], offset: 10); - final sel = Selection(start: start, end: end); - - final collapsedSelAtStart = sel.collapse(atStart: true); - expect(collapsedSelAtStart.start, start); - expect(collapsedSelAtStart.end, start); - - final collapsedSelAtEnd = sel.collapse(); - expect(collapsedSelAtEnd.start, end); - expect(collapsedSelAtEnd.end, end); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/operation_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/operation_test.dart deleted file mode 100644 index 2516240b17..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/operation_test.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/src/core/document/node.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/src/core/transform/operation.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/core/document/document.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - group('transform path', () { - test('transform path changed', () { - expect(transformPath([0, 1], [0, 1]), [0, 2]); - expect(transformPath([0, 1], [0, 2]), [0, 3]); - expect(transformPath([0, 1], [0, 2, 7, 8, 9]), [0, 3, 7, 8, 9]); - expect(transformPath([0, 1, 2], [0, 0, 7, 8, 9]), [0, 0, 7, 8, 9]); - }); - test("transform path not changed", () { - expect(transformPath([0, 1, 2], [0, 0, 7, 8, 9]), [0, 0, 7, 8, 9]); - expect(transformPath([0, 1, 2], [0, 1]), [0, 1]); - expect(transformPath([1, 1], [1, 0]), [1, 0]); - }); - test("transform path delta", () { - expect(transformPath([0, 1], [0, 1], 5), [0, 6]); - }); - }); - group('transform operation', () { - test('insert + insert', () { - final t = transformOperation( - InsertOperation([0, 1], - [Node(type: "node", attributes: {}, children: LinkedList())]), - InsertOperation([0, 1], - [Node(type: "node", attributes: {}, children: LinkedList())])); - expect(t.path, [0, 2]); - }); - test('delete + delete', () { - final t = transformOperation( - DeleteOperation([0, 1], - [Node(type: "node", attributes: {}, children: LinkedList())]), - DeleteOperation([0, 2], - [Node(type: "node", attributes: {}, children: LinkedList())])); - expect(t.path, [0, 1]); - }); - }); - test('transform transaction builder', () { - final item1 = Node(type: "node", attributes: {}, children: LinkedList()); - final item2 = Node(type: "node", attributes: {}, children: LinkedList()); - final item3 = Node(type: "node", attributes: {}, children: LinkedList()); - final root = Node( - type: "root", - attributes: {}, - children: LinkedList() - ..addAll([ - item1, - item2, - item3, - ]), - ); - final state = EditorState(document: Document(root: root)); - - expect(item1.path, [0]); - expect(item2.path, [1]); - expect(item3.path, [2]); - - final transaction = state.transaction; - transaction.deleteNode(item1); - transaction.deleteNode(item2); - transaction.deleteNode(item3); - state.apply(transaction); - expect(transaction.operations[0].path, [0]); - expect(transaction.operations[1].path, [0]); - expect(transaction.operations[2].path, [0]); - }); - group("toJson", () { - test("insert", () { - final root = Node(type: "root", attributes: {}, children: LinkedList()); - final state = EditorState(document: Document(root: root)); - - final item1 = Node(type: "node", attributes: {}, children: LinkedList()); - final transaction = state.transaction; - transaction.insertNode([0], item1); - state.apply(transaction); - expect(transaction.toJson(), { - "operations": [ - { - "op": "insert", - "path": [0], - "nodes": [item1.toJson()], - } - ] - }); - }); - test("delete", () { - final item1 = Node(type: "node", attributes: {}, children: LinkedList()); - final root = Node( - type: "root", - attributes: {}, - children: LinkedList() - ..addAll([ - item1, - ]), - ); - final state = EditorState(document: Document(root: root)); - final transaction = state.transaction; - transaction.deleteNode(item1); - state.apply(transaction); - expect(transaction.toJson(), { - "operations": [ - { - "op": "delete", - "path": [0], - "nodes": [item1.toJson()], - } - ], - }); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/undo_manager_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/undo_manager_test.dart deleted file mode 100644 index 6c7332164f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/legacy/undo_manager_test.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'dart:collection'; -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/history/undo_manager.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - Node createEmptyEditorRoot() { - return Node( - type: 'editor', - children: LinkedList(), - attributes: {}, - ); - } - - test("HistoryItem #1", () { - final document = Document(root: createEmptyEditorRoot()); - final editorState = EditorState(document: document); - - final historyItem = HistoryItem(); - historyItem - .add(DeleteOperation([0], [TextNode(delta: Delta()..insert('0'))])); - historyItem - .add(DeleteOperation([0], [TextNode(delta: Delta()..insert('1'))])); - historyItem - .add(DeleteOperation([0], [TextNode(delta: Delta()..insert('2'))])); - - final transaction = historyItem.toTransaction(editorState); - assert(isInsertAndPathEqual(transaction.operations[0], [0], '2')); - assert(isInsertAndPathEqual(transaction.operations[1], [0], '1')); - assert(isInsertAndPathEqual(transaction.operations[2], [0], '0')); - }); - - test("HistoryItem #2", () { - final document = Document(root: createEmptyEditorRoot()); - final editorState = EditorState(document: document); - - final historyItem = HistoryItem(); - historyItem - .add(DeleteOperation([0], [TextNode(delta: Delta()..insert('0'))])); - historyItem - .add(UpdateOperation([0], {"subType": "number"}, {"subType": null})); - historyItem.add(DeleteOperation([0], [TextNode.empty(), TextNode.empty()])); - historyItem.add(DeleteOperation([0], [TextNode.empty()])); - - final transaction = historyItem.toTransaction(editorState); - assert(isInsertAndPathEqual(transaction.operations[0], [0])); - assert(isInsertAndPathEqual(transaction.operations[1], [0])); - assert(transaction.operations[2] is UpdateOperation); - assert(isInsertAndPathEqual(transaction.operations[3], [0], '0')); - }); -} - -bool isInsertAndPathEqual(Operation operation, Path path, [String? content]) { - if (operation is! InsertOperation) { - return false; - } - - if (!operation.path.equals(path)) { - return false; - } - - final firstNode = operation.nodes.first; - if (firstNode is! TextNode) { - return false; - } - - if (content == null) { - return true; - } - - return firstNode.delta.toPlainText() == content; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/decoder/delta_markdown_decoder_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/decoder/delta_markdown_decoder_test.dart deleted file mode 100644 index 206aa0ea0b..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/decoder/delta_markdown_decoder_test.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('delta_markdown_decoder.dart', () { - test('bold', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.bold: true, - }), - ]); - final result = DeltaMarkdownDecoder().convert('Welcome to **AppFlowy**'); - expect(result, delta); - }); - - test('italic', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.italic: true, - }), - ]); - final result = DeltaMarkdownDecoder().convert('Welcome to _AppFlowy_'); - expect(result, delta); - }); - - test('strikethrough', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.strikethrough: true, - }), - ]); - final result = DeltaMarkdownDecoder().convert('Welcome to ~~AppFlowy~~'); - expect(result, delta); - }); - - test('href', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.href: 'https://appflowy.io', - }), - ]); - final result = DeltaMarkdownDecoder() - .convert('Welcome to [AppFlowy](https://appflowy.io)'); - expect(result, delta); - }); - - test('code', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.code: true, - }), - ]); - final result = DeltaMarkdownDecoder().convert('Welcome to `AppFlowy`'); - expect(result, delta); - }); - - test('bold', () { - const markdown = - '***`Welcome`*** ***~~to~~*** ***[AppFlowy](https://appflowy.io)***'; - final delta = Delta(operations: [ - TextInsert('', attributes: { - BuiltInAttributeKey.italic: true, - BuiltInAttributeKey.bold: true, - }), - TextInsert('Welcome', attributes: { - BuiltInAttributeKey.code: true, - BuiltInAttributeKey.italic: true, - BuiltInAttributeKey.bold: true, - }), - TextInsert('', attributes: { - BuiltInAttributeKey.italic: true, - BuiltInAttributeKey.bold: true, - }), - TextInsert(' '), - TextInsert('to', attributes: { - BuiltInAttributeKey.italic: true, - BuiltInAttributeKey.bold: true, - BuiltInAttributeKey.strikethrough: true, - }), - TextInsert(' '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.href: 'https://appflowy.io', - BuiltInAttributeKey.bold: true, - BuiltInAttributeKey.italic: true, - }), - ]); - final result = DeltaMarkdownDecoder().convert(markdown); - expect(result, delta); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/decoder/document_markdown_decoder_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/decoder/document_markdown_decoder_test.dart deleted file mode 100644 index a95e7b877e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/decoder/document_markdown_decoder_test.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/src/plugins/markdown/decoder/document_markdown_decoder.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('document_markdown_decoder.dart', () { - const example = ''' -{ - "document": { - "type": "editor", - "children": [ - { - "type": "text", - "attributes": {"subtype": "heading", "heading": "h2"}, - "delta": [ - {"insert": "👋 "}, - {"insert": "Welcome to", "attributes": {"bold": true}}, - {"insert": " "}, - { - "insert": "AppFlowy Editor", - "attributes": {"italic": true, "bold": true, "href": "appflowy.io"} - } - ] - }, - {"type": "text", "delta": []}, - { - "type": "text", - "delta": [ - {"insert": "AppFlowy Editor is a "}, - {"insert": "highly customizable", "attributes": {"bold": true}}, - {"insert": " "}, - {"insert": "rich-text editor", "attributes": {"italic": true}} - ] - }, - { - "type": "text", - "attributes": {"subtype": "checkbox", "checkbox": true}, - "delta": [{"insert": "Customizable"}] - }, - { - "type": "text", - "attributes": {"subtype": "checkbox", "checkbox": true}, - "delta": [{"insert": "Test-covered"}] - }, - { - "type": "text", - "attributes": {"subtype": "checkbox", "checkbox": false}, - "delta": [{"insert": "more to come!"}] - }, - {"type": "text", "delta": []}, - { - "type": "text", - "attributes": {"subtype": "quote"}, - "delta": [{"insert": "Here is an example you can give a try"}] - }, - {"type": "text", "delta": []}, - { - "type": "text", - "delta": [ - {"insert": "You can also use "}, - { - "insert": "AppFlowy Editor", - "attributes": {"italic": true, "bold": true} - }, - {"insert": " as a component to build your own app."} - ] - }, - {"type": "text", "delta": []}, - { - "type": "text", - "attributes": {"subtype": "bulleted-list"}, - "delta": [{"insert": "Use / to insert blocks"}] - }, - { - "type": "text", - "attributes": {"subtype": "bulleted-list"}, - "delta": [ - { - "insert": "Select text to trigger to the toolbar to format your notes." - } - ] - }, - {"type": "text", "delta": []}, - { - "type": "text", - "delta": [ - { - "insert": "If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!" - } - ] - }, - {"type": "text", "delta": []}, - {"type": "text", "delta": [{"insert": ""}]} - ] - } -} -'''; - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - test('parser document', () async { - const markdown = ''' -## 👋 **Welcome to** ***[AppFlowy Editor](appflowy.io)*** - -AppFlowy Editor is a **highly customizable** _rich-text editor_ -- [x] Customizable -- [x] Test-covered -- [ ] more to come! - -> Here is an example you can give a try - -You can also use ***AppFlowy Editor*** as a component to build your own app. - -* Use / to insert blocks -* Select text to trigger to the toolbar to format your notes. - -If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders! -'''; - final result = DocumentMarkdownDecoder().convert(markdown); - final data = Map.from(json.decode(example)); - expect(result.toJson(), data); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/delta_markdown_encoder_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/delta_markdown_encoder_test.dart deleted file mode 100644 index 831a449d02..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/delta_markdown_encoder_test.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('delta_markdown_encoder.dart', () { - test('bold', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.bold: true, - }), - ]); - final result = DeltaMarkdownEncoder().convert(delta); - expect(result, 'Welcome to **AppFlowy**'); - }); - - test('italic', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.italic: true, - }), - ]); - final result = DeltaMarkdownEncoder().convert(delta); - expect(result, 'Welcome to _AppFlowy_'); - }); - - test('underline', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.underline: true, - }), - ]); - final result = DeltaMarkdownEncoder().convert(delta); - expect(result, 'Welcome to AppFlowy'); - }); - - test('strikethrough', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.strikethrough: true, - }), - ]); - final result = DeltaMarkdownEncoder().convert(delta); - expect(result, 'Welcome to ~~AppFlowy~~'); - }); - - test('href', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.href: 'https://appflowy.io', - }), - ]); - final result = DeltaMarkdownEncoder().convert(delta); - expect(result, 'Welcome to [AppFlowy](https://appflowy.io)'); - }); - - test('code', () { - final delta = Delta(operations: [ - TextInsert('Welcome to '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.code: true, - }), - ]); - final result = DeltaMarkdownEncoder().convert(delta); - expect(result, 'Welcome to `AppFlowy`'); - }); - - test('composition', () { - final delta = Delta(operations: [ - TextInsert('Welcome', attributes: { - BuiltInAttributeKey.code: true, - BuiltInAttributeKey.italic: true, - BuiltInAttributeKey.bold: true, - BuiltInAttributeKey.underline: true, - }), - TextInsert(' '), - TextInsert('to', attributes: { - BuiltInAttributeKey.italic: true, - BuiltInAttributeKey.bold: true, - BuiltInAttributeKey.strikethrough: true, - }), - TextInsert(' '), - TextInsert('AppFlowy', attributes: { - BuiltInAttributeKey.href: 'https://appflowy.io', - BuiltInAttributeKey.bold: true, - BuiltInAttributeKey.italic: true, - }), - ]); - final result = DeltaMarkdownEncoder().convert(delta); - expect( - result, - '***`Welcome`*** ***~~to~~*** ***[AppFlowy](https://appflowy.io)***', - ); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/document_markdown_encoder_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/document_markdown_encoder_test.dart deleted file mode 100644 index 5b104f3226..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/document_markdown_encoder_test.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('document_markdown_encoder.dart', () { - const example = ''' -{ - "document": { - "type": "editor", - "children": [ - { - "type": "text", - "attributes": { - "subtype": "heading", - "heading": "h2" - }, - "delta": [ - { "insert": "👋 " }, - { "insert": "Welcome to", "attributes": { "bold": true } }, - { "insert": " " }, - { - "insert": "AppFlowy Editor", - "attributes": { - "href": "appflowy.io", - "italic": true, - "bold": true - } - } - ] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "delta": [ - { "insert": "AppFlowy Editor is a " }, - { "insert": "highly customizable", "attributes": { "bold": true } }, - { "insert": " " }, - { "insert": "rich-text editor", "attributes": { "italic": true } }, - { "insert": " for " }, - { "insert": "Flutter", "attributes": { "underline": true } } - ] - }, - { - "type": "text", - "attributes": { "checkbox": true, "subtype": "checkbox" }, - "delta": [{ "insert": "Customizable" }] - }, - { - "type": "text", - "attributes": { "checkbox": true, "subtype": "checkbox" }, - "delta": [{ "insert": "Test-covered" }] - }, - { - "type": "text", - "attributes": { "checkbox": false, "subtype": "checkbox" }, - "delta": [{ "insert": "more to come!" }] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "attributes": { "subtype": "quote" }, - "delta": [{ "insert": "Here is an example you can give a try" }] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "delta": [ - { "insert": "You can also use " }, - { - "insert": "AppFlowy Editor", - "attributes": { - "italic": true, - "bold": true, - "backgroundColor": "0x6000BCF0" - } - }, - { "insert": " as a component to build your own app." } - ] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "attributes": { "subtype": "bulleted-list" }, - "delta": [{ "insert": "Use / to insert blocks" }] - }, - { - "type": "text", - "attributes": { "subtype": "bulleted-list" }, - "delta": [ - { - "insert": "Select text to trigger to the toolbar to format your notes." - } - ] - }, - { "type": "text", "delta": [] }, - { - "type": "text", - "delta": [ - { - "insert": "If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!" - } - ] - } - ] - } -} -'''; - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - test('parser document', () async { - final data = Map.from(json.decode(example)); - final document = Document.fromJson(data); - final result = DocumentMarkdownEncoder(parsers: [ - const TextNodeParser(), - const ImageNodeParser(), - ]).convert(document); - expect(result, ''' -## 👋 **Welcome to** ***[AppFlowy Editor](appflowy.io)*** - -AppFlowy Editor is a **highly customizable** _rich-text editor_ for Flutter -- [x] Customizable -- [x] Test-covered -- [ ] more to come! - -> Here is an example you can give a try - -You can also use ***AppFlowy Editor*** as a component to build your own app. - -* Use / to insert blocks -* Select text to trigger to the toolbar to format your notes. - -If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!'''); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/parser/image_node_parser_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/parser/image_node_parser_test.dart deleted file mode 100644 index 77102c8310..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/parser/image_node_parser_test.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('image_node_parser.dart', () { - test('parser image node', () { - final node = Node( - type: 'image', - attributes: { - 'image_src': 'https://appflowy.io', - }, - ); - final result = const ImageNodeParser().transform(node); - expect(result, '![](https://appflowy.io)'); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/parser/text_node_parser_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/parser/text_node_parser_test.dart deleted file mode 100644 index 4932dbde6f..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/markdown/encoder/parser/text_node_parser_test.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('text_node_parser.dart', () { - const text = 'Welcome to AppFlowy'; - - test('heading style', () { - final h1 = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h1, - }, - ); - final h2 = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h2, - }, - ); - final h3 = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h3, - }, - ); - expect(const TextNodeParser().transform(h1), '# $text'); - expect(const TextNodeParser().transform(h2), '## $text'); - expect(const TextNodeParser().transform(h3), '### $text'); - }); - - test('bulleted list style', () { - final node = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }, - ); - expect(const TextNodeParser().transform(node), '* $text'); - }); - - test('number list style', () { - final node = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.numberList, - BuiltInAttributeKey.number: 1, - }, - ); - expect(const TextNodeParser().transform(node), '1. $text'); - }); - - test('checkbox style', () { - final checkbox = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: true, - }, - ); - final unCheckbox = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - ); - expect(const TextNodeParser().transform(checkbox), '- [x] $text'); - expect(const TextNodeParser().transform(unCheckbox), '- [ ] $text'); - }); - - test('quote style', () { - final node = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }, - ); - expect(const TextNodeParser().transform(node), '> $text'); - }); - - test('code block style', () { - final node = TextNode( - delta: Delta(operations: [TextInsert(text)]), - attributes: { - BuiltInAttributeKey.subtype: 'code_block', - }, - ); - expect(const TextNodeParser().transform(node), '```\n$text\n```'); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/quill_delta/delta_document_encoder_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/quill_delta/delta_document_encoder_test.dart deleted file mode 100644 index 1fa4b3d95d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/plugins/quill_delta/delta_document_encoder_test.dart +++ /dev/null @@ -1,551 +0,0 @@ -import 'dart:convert'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - group('delta_document_encoder.dart', () { - test('', () { - final json = jsonDecode(quillDeltaSample.replaceAll('\\\\\n', '\\n')); - final document = DeltaDocumentConvert().convertFromJSON(json); - expect(jsonEncode(document.toJson()), documentSample); - }); - }); -} - -const documentSample = - r'''{"document":{"type":"editor","children":[{"type":"text","attributes":{"subtype":"heading","heading":"h1"},"delta":[{"insert":"Flutter Quill"}]},{"type":"text","delta":[]},{"type":"text","attributes":{"subtype":"heading","heading":"h2"},"delta":[{"insert":"Rich text editor for Flutter"}]},{"type":"text","attributes":{"subtype":"heading","heading":"h3"},"delta":[{"insert":"Quill component for Flutter"}]},{"type":"text","delta":[{"insert":"This "},{"insert":"library","attributes":{"italic":true}},{"insert":" supports "},{"insert":"mobile","attributes":{"bold":true,"backgroundColor":"0xFFebd6ff"}},{"insert":" platform "},{"insert":"only","attributes":{"underline":true,"bold":true,"color":"0xFFe60000"}},{"insert":" and ","attributes":{"color":"0xd7000000"}},{"insert":"web","attributes":{"strikethrough":true}},{"insert":" is not supported."}]},{"type":"text","delta":[{"insert":"You are welcome to use "},{"insert":"Bullet Journal","attributes":{"href":"https://bulletjournal.us/home/index.html"}},{"insert":":"}]},{"type":"text","attributes":{"subtype":"number-list","number":1},"delta":[{"insert":"Track personal and group journals (ToDo, Note, Ledger) from multiple views with timely reminders"}]},{"type":"text","attributes":{"subtype":"number-list","number":2},"delta":[{"insert":"Share your tasks and notes with teammates, and see changes as they happen in real-time, across all devices"}]},{"type":"text","attributes":{"subtype":"number-list","number":3},"delta":[{"insert":"Check out what you and your teammates are working on each day"}]},{"type":"text","delta":[]},{"type":"text","attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"Splitting bills with friends can never be easier."}]},{"type":"text","attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"Start creating a group and invite your friends to join."}]},{"type":"text","attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"Create a BuJo of Ledger type to see expense or balance summary."}]},{"type":"text","delta":[]},{"type":"text","attributes":{"subtype":"quote"},"delta":[{"insert":"Attach one or multiple labels to tasks, notes or transactions. Later you can track them just using the label(s)."}]},{"type":"text","delta":[]},{"type":"text","delta":[{"insert":"var BuJo = 'Bullet' + 'Journal'"}]},{"type":"text","delta":[]},{"type":"text","delta":[{"insert":" Start tracking in your browser"}]},{"type":"text","delta":[{"insert":" Stop the timer on your phone"}]},{"type":"text","delta":[{"insert":" All your time entries are synced"}]},{"type":"text","delta":[{"insert":" between the phone apps"}]},{"type":"text","delta":[{"insert":" and the website."}]},{"type":"text","delta":[]},{"type":"text","delta":[]},{"type":"text","delta":[{"insert":"Center Align"}]},{"type":"text","delta":[{"insert":"Right Align"}]},{"type":"text","delta":[{"insert":"Justify Align"}]},{"type":"text","attributes":{"subtype":"number-list","number":1},"delta":[{"insert":"Have trouble finding things? "}]},{"type":"text","attributes":{"subtype":"number-list","number":2},"delta":[{"insert":"Just type in the search bar"}]},{"type":"text","attributes":{"subtype":"number-list","number":3},"delta":[{"insert":"and easily find contents"}]},{"type":"text","attributes":{"subtype":"number-list","number":4},"delta":[{"insert":"across projects or folders."}]},{"type":"text","attributes":{"subtype":"number-list","number":5},"delta":[{"insert":"It matches text in your note or task."}]},{"type":"text","attributes":{"subtype":"number-list","number":6},"delta":[{"insert":"Enable reminders so that you will get notified by"}]},{"type":"text","attributes":{"subtype":"number-list","number":7},"delta":[{"insert":"email"}]},{"type":"text","attributes":{"subtype":"number-list","number":8},"delta":[{"insert":"message on your phone"}]},{"type":"text","attributes":{"subtype":"number-list","number":9},"delta":[{"insert":"popup on the web site"}]},{"type":"text","children":[{"type":"text","children":[{"type":"text","attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"tasks"}]},{"type":"text","attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"notes"}]},{"type":"text","children":[{"type":"text","attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"under BuJo "}]}],"attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"transactions"}]}],"attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"Organize your"}]}],"attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"Create a BuJo serving as project or folder"}]},{"type":"text","children":[{"type":"text","attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"or hierarchical view"}]}],"attributes":{"subtype":"bulleted-list"},"delta":[{"insert":"See them in Calendar"}]},{"type":"text","attributes":{"subtype":"checkbox","checkbox":true},"delta":[{"insert":"this is a check list"}]},{"type":"text","attributes":{"subtype":"checkbox","checkbox":false},"delta":[{"insert":"this is a uncheck list"}]},{"type":"text","delta":[{"insert":"Font Sans Serif Serif Monospace Size Small Large Hugefont size 15 font size 35 font size 20 diff-match-patch"}]},{"type":"text","delta":[{"insert":""}]}]}}'''; - -const quillDeltaSample = r''' -[ - { - "insert": "Flutter Quill" - }, - { - "attributes": { - "header": 1 - }, - "insert": "\n" - }, - { - "insert": { - "video": "https://www.youtube.com/watch?v=V4hgdKhIqtc&list=PLbhaS_83B97s78HsDTtplRTEhcFsqSqIK&index=1" - } - }, - { - "insert": { - "video": "https://user-images.githubusercontent.com/122956/126238875-22e42501-ad41-4266-b1d6-3f89b5e3b79b.mp4" - } - }, - { - "insert": "\nRich text editor for Flutter" - }, - { - "attributes": { - "header": 2 - }, - "insert": "\n" - }, - { - "insert": "Quill component for Flutter" - }, - { - "attributes": { - "header": 3 - }, - "insert": "\n" - }, - { - "insert": "This " - }, - { - "attributes": { - "italic": true, - "background": "transparent" - }, - "insert": "library" - }, - { - "insert": " supports " - }, - { - "attributes": { - "bold": true, - "background": "#ebd6ff" - }, - "insert": "mobile" - }, - { - "insert": " platform " - }, - { - "attributes": { - "underline": true, - "bold": true, - "color": "#e60000" - }, - "insert": "only" - }, - { - "attributes": { - "color": "rgba(0, 0, 0, 0.847)" - }, - "insert": " and " - }, - { - "attributes": { - "strike": true, - "color": "black" - }, - "insert": "web" - }, - { - "insert": " is not supported.\nYou are welcome to use " - }, - { - "attributes": { - "link": "https://bulletjournal.us/home/index.html" - }, - "insert": "Bullet Journal" - }, - { - "insert": ":\nTrack personal and group journals (ToDo, Note, Ledger) from multiple views with timely reminders" - }, - { - "attributes": { - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "Share your tasks and notes with teammates, and see changes as they happen in real-time, across all devices" - }, - { - "attributes": { - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "Check out what you and your teammates are working on each day" - }, - { - "attributes": { - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "\nSplitting bills with friends can never be easier." - }, - { - "attributes": { - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "Start creating a group and invite your friends to join." - }, - { - "attributes": { - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "Create a BuJo of Ledger type to see expense or balance summary." - }, - { - "attributes": { - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "\nAttach one or multiple labels to tasks, notes or transactions. Later you can track them just using the label(s)." - }, - { - "attributes": { - "blockquote": true - }, - "insert": "\n" - }, - { - "insert": "\nvar BuJo = 'Bullet' + 'Journal'" - }, - { - "attributes": { - "code-block": true - }, - "insert": "\n" - }, - { - "insert": "\nStart tracking in your browser" - }, - { - "attributes": { - "indent": 1 - }, - "insert": "\n" - }, - { - "insert": "Stop the timer on your phone" - }, - { - "attributes": { - "indent": 1 - }, - "insert": "\n" - }, - { - "insert": "All your time entries are synced" - }, - { - "attributes": { - "indent": 2 - }, - "insert": "\n" - }, - { - "insert": "between the phone apps" - }, - { - "attributes": { - "indent": 2 - }, - "insert": "\n" - }, - { - "insert": "and the website." - }, - { - "attributes": { - "indent": 3 - }, - "insert": "\n" - }, - { - "insert": "\n" - }, - { - "insert": "\nCenter Align" - }, - { - "attributes": { - "align": "center" - }, - "insert": "\n" - }, - { - "insert": "Right Align" - }, - { - "attributes": { - "align": "right" - }, - "insert": "\n" - }, - { - "insert": "Justify Align" - }, - { - "attributes": { - "align": "justify" - }, - "insert": "\n" - }, - { - "insert": "Have trouble finding things? " - }, - { - "attributes": { - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "Just type in the search bar" - }, - { - "attributes": { - "indent": 1, - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "and easily find contents" - }, - { - "attributes": { - "indent": 2, - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "across projects or folders." - }, - { - "attributes": { - "indent": 2, - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "It matches text in your note or task." - }, - { - "attributes": { - "indent": 1, - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "Enable reminders so that you will get notified by" - }, - { - "attributes": { - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "email" - }, - { - "attributes": { - "indent": 1, - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "message on your phone" - }, - { - "attributes": { - "indent": 1, - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "popup on the web site" - }, - { - "attributes": { - "indent": 1, - "list": "ordered" - }, - "insert": "\n" - }, - { - "insert": "Create a BuJo serving as project or folder" - }, - { - "attributes": { - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "Organize your" - }, - { - "attributes": { - "indent": 1, - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "tasks" - }, - { - "attributes": { - "indent": 2, - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "notes" - }, - { - "attributes": { - "indent": 2, - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "transactions" - }, - { - "attributes": { - "indent": 2, - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "under BuJo " - }, - { - "attributes": { - "indent": 3, - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "See them in Calendar" - }, - { - "attributes": { - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "or hierarchical view" - }, - { - "attributes": { - "indent": 1, - "list": "bullet" - }, - "insert": "\n" - }, - { - "insert": "this is a check list" - }, - { - "attributes": { - "list": "checked" - }, - "insert": "\n" - }, - { - "insert": "this is a uncheck list" - }, - { - "attributes": { - "list": "unchecked" - }, - "insert": "\n" - }, - { - "insert": "Font " - }, - { - "attributes": { - "font": "sans-serif" - }, - "insert": "Sans Serif" - }, - { - "insert": " " - }, - { - "attributes": { - "font": "serif" - }, - "insert": "Serif" - }, - { - "insert": " " - }, - { - "attributes": { - "font": "monospace" - }, - "insert": "Monospace" - }, - { - "insert": " Size " - }, - { - "attributes": { - "size": "small" - }, - "insert": "Small" - }, - { - "insert": " " - }, - { - "attributes": { - "size": "large" - }, - "insert": "Large" - }, - { - "insert": " " - }, - { - "attributes": { - "size": "huge" - }, - "insert": "Huge" - }, - { - "attributes": { - "size": "15.0" - }, - "insert": "font size 15" - }, - { - "insert": " " - }, - { - "attributes": { - "size": "35" - }, - "insert": "font size 35" - }, - { - "insert": " " - }, - { - "attributes": { - "size": "20" - }, - "insert": "font size 20" - }, - { - "attributes": { - "token": "built_in" - }, - "insert": " diff" - }, - { - "attributes": { - "token": "operator" - }, - "insert": "-match" - }, - { - "attributes": { - "token": "literal" - }, - "insert": "-patch" - }, - { - "insert": { - "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png" - }, - "attributes": { - "width": "230", - "style": "display: block; margin: auto;" - } - }, - { - "insert": "\n" - } -] -'''; diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/action_menu/action_menu_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/action_menu/action_menu_test.dart deleted file mode 100644 index 9725b5e0f9..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/action_menu/action_menu_test.dart +++ /dev/null @@ -1,165 +0,0 @@ -import 'package:appflowy_editor/src/render/action_menu/action_menu.dart'; -import 'package:appflowy_editor/src/render/action_menu/action_menu_item.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:provider/provider.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('action_menu.dart', () { - testWidgets('hover and tap action', (tester) async { - var actionHit = false; - - final widget = ActionMenuOverlay( - items: [ - ActionMenuItem.icon( - iconData: Icons.download, - onPressed: () => actionHit = true, - ) - ], - child: const SizedBox( - height: 100, - width: 100, - ), - ); - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: ChangeNotifierProvider( - create: (context) => ActionMenuState([]), - child: widget, - ), - ), - ), - ); - expect(find.byType(ActionMenuWidget), findsNothing); - - final actionMenuOverlay = find.byType(ActionMenuOverlay); - - final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - await gesture.addPointer(location: Offset.zero); - await tester.pump(); - await gesture.moveTo(tester.getCenter(actionMenuOverlay)); - await tester.pumpAndSettle(); - - final actionMenu = find.byType(ActionMenuWidget); - expect(actionMenu, findsOneWidget); - - final action = find.descendant( - of: actionMenu, - matching: find.byType(ActionMenuItemWidget), - ); - expect(action, findsOneWidget); - - await tester.tap(action); - expect(actionHit, true); - }); - - testWidgets('stacked action menu overlays', (tester) async { - final childWidget = ChangeNotifierProvider( - create: (context) => ActionMenuState([0, 0]), - child: ActionMenuOverlay( - items: [ - ActionMenuItem( - iconBuilder: ({color, size}) => const Text("child"), - onPressed: null, - ) - ], - child: const SizedBox( - height: 100, - width: 100, - ), - ), - ); - - final parentWidget = ChangeNotifierProvider( - create: (context) => ActionMenuState([0]), - child: ActionMenuOverlay( - items: [ - ActionMenuItem( - iconBuilder: ({color, size}) => const Text("parent"), - onPressed: null, - ) - ], - child: childWidget, - ), - ); - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: Center(child: parentWidget), - ), - ), - ); - expect(find.byType(ActionMenuWidget), findsNothing); - - final overlays = find.byType(ActionMenuOverlay); - expect( - tester.getCenter(overlays.at(0)), - tester.getCenter(overlays.at(1)), - ); - - final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - await gesture.addPointer(location: Offset.zero); - await tester.pump(); - await gesture.moveTo(tester.getCenter(overlays.at(0))); - await tester.pumpAndSettle(); - - final actionMenu = find.byType(ActionMenuWidget); - expect(actionMenu, findsOneWidget); - - expect(find.text("child"), findsOneWidget); - expect(find.text("parent"), findsNothing); - }); - - testWidgets('customActionMenuBuilder', (tester) async { - final widget = ActionMenuOverlay( - items: [ - ActionMenuItem.icon( - iconData: Icons.download, - onPressed: null, - ) - ], - customActionMenuBuilder: (context, items) { - return const Positioned.fill( - child: Center( - child: Text("custom"), - ), - ); - }, - child: const SizedBox( - height: 100, - width: 100, - ), - ); - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: ChangeNotifierProvider( - create: (context) => ActionMenuState([]), - child: widget, - ), - ), - ), - ); - expect(find.text("custom"), findsNothing); - - final actionMenuOverlay = find.byType(ActionMenuOverlay); - - final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - await gesture.addPointer(location: Offset.zero); - await tester.pump(); - await gesture.moveTo(tester.getCenter(actionMenuOverlay)); - await tester.pumpAndSettle(); - - expect(find.text("custom"), findsOneWidget); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/image/image_node_builder_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/image/image_node_builder_test.dart deleted file mode 100644 index 118d30e362..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/image/image_node_builder_test.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:appflowy_editor/src/service/editor_service.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:network_image_mock/network_image_mock.dart'; - -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('image_node_builder.dart', () { - testWidgets('render image node', (tester) async { - mockNetworkImagesFor(() async { - const text = 'Welcome to Appflowy 😁'; - const src = - 'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'; - final editor = tester.editor - ..insertTextNode(text) - ..insertImageNode(src) - ..insertTextNode(text); - await editor.startTesting(); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 3); - expect(find.byType(Image), findsOneWidget); - }); - }); - - testWidgets('render image align', (tester) async { - mockNetworkImagesFor(() async { - const text = 'Welcome to Appflowy 😁'; - const src = - 'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'; - final editor = tester.editor - ..insertTextNode(text) - ..insertImageNode(src, align: 'left', width: 100) - ..insertImageNode(src, align: 'center', width: 100) - ..insertImageNode(src, align: 'right', width: 100) - ..insertTextNode(text); - await editor.startTesting(); - await tester.pumpAndSettle(); - - expect(editor.documentLength, 5); - final imageFinder = find.byType(Image); - expect(imageFinder, findsNWidgets(3)); - - final editorFinder = find.byType(AppFlowyEditor); - final editorRect = tester.getRect(editorFinder); - - final leftImageRect = tester.getRect(imageFinder.at(0)); - expect( - leftImageRect.left, editor.editorState.editorStyle.padding!.left); - final rightImageRect = tester.getRect(imageFinder.at(2)); - expect(rightImageRect.right, - editorRect.right - editor.editorState.editorStyle.padding!.right); - final centerImageRect = tester.getRect(imageFinder.at(1)); - expect(centerImageRect.left, - (leftImageRect.left + rightImageRect.left) / 2.0); - expect(leftImageRect.size, centerImageRect.size); - expect(rightImageRect.size, centerImageRect.size); - - final leftImageNode = editor.document.nodeAtPath([1]); - - expect(editor.runAction(1, leftImageNode!), true); // align center - await tester.pump(); - expect( - tester.getRect(imageFinder.at(0)).left, - centerImageRect.left, - ); - - expect(editor.runAction(2, leftImageNode), true); // align right - await tester.pump(); - expect( - tester.getRect(imageFinder.at(0)).right, - rightImageRect.right, - ); - }); - }); - - testWidgets('render image copy', (tester) async { - mockNetworkImagesFor(() async { - const text = 'Welcome to Appflowy 😁'; - const src = - 'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'; - final editor = tester.editor - ..insertTextNode(text) - ..insertImageNode(src) - ..insertTextNode(text); - await editor.startTesting(); - - expect(editor.documentLength, 3); - final imageFinder = find.byType(Image); - expect(imageFinder, findsOneWidget); - - final imageNode = editor.document.nodeAtPath([1]); - - expect(editor.runAction(3, imageNode!), true); // copy - await tester.pump(); - }); - }); - - testWidgets('render image delete', (tester) async { - mockNetworkImagesFor(() async { - const text = 'Welcome to Appflowy 😁'; - const src = - 'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'; - final editor = tester.editor - ..insertTextNode(text) - ..insertImageNode(src) - ..insertImageNode(src) - ..insertTextNode(text); - await editor.startTesting(); - - expect(editor.documentLength, 4); - final imageFinder = find.byType(Image); - expect(imageFinder, findsNWidgets(2)); - - final imageNode = editor.document.nodeAtPath([1]); - expect(editor.runAction(4, imageNode!), true); // delete - - await tester.pump(const Duration(milliseconds: 100)); - expect(editor.documentLength, 3); - expect(find.byType(Image), findsNWidgets(1)); - }); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/image/image_node_widget_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/image/image_node_widget_test.dart deleted file mode 100644 index f758983f78..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/image/image_node_widget_test.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/image/image_node_widget.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:network_image_mock/network_image_mock.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('image_node_widget.dart', () { - testWidgets('build the image node widget', (tester) async { - mockNetworkImagesFor(() async { - const src = - 'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'; - - final widget = ImageNodeWidget( - src: src, - width: 100, - node: Node( - type: 'image', - children: LinkedList(), - attributes: { - 'image_src': src, - 'align': 'center', - }, - ), - alignment: Alignment.center, - onResize: (width) {}, - ); - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: widget, - ), - ), - ); - await tester.pumpAndSettle(); - - final imageNodeFinder = find.byType(ImageNodeWidget); - expect(imageNodeFinder, findsOneWidget); - - final imageFinder = find.byType(Image); - expect(imageFinder, findsOneWidget); - - final imageNodeRect = tester.getRect(imageNodeFinder); - final imageRect = tester.getRect(imageFinder); - - expect(imageRect.width, 100); - expect((imageNodeRect.left - imageRect.left).abs(), - (imageNodeRect.right - imageRect.right).abs()); - }); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/link_menu/link_menu_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/link_menu/link_menu_test.dart deleted file mode 100644 index 367e7702c6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/link_menu/link_menu_test.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/link_menu/link_menu.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('link_menu.dart', () { - testWidgets('test empty link menu actions', (tester) async { - const link = 'appflowy.io'; - var submittedText = ''; - final linkMenu = LinkMenu( - onOpenLink: () {}, - onCopyLink: () {}, - onRemoveLink: () {}, - onFocusChange: (value) {}, - onSubmitted: (text) { - submittedText = text; - }, - ); - await tester.pumpWidget( - MaterialApp( - home: Material( - child: linkMenu, - ), - ), - ); - - expect(find.byType(TextButton), findsNothing); - expect(find.byType(TextField), findsOneWidget); - - await tester.tap(find.byType(TextField)); - await tester.enterText(find.byType(TextField), link); - await tester.pumpAndSettle(); - await tester.testTextInput.receiveAction(TextInputAction.done); - await tester.pumpAndSettle(); - - expect(submittedText, link); - }); - - testWidgets('test tap linked text', (tester) async { - const link = 'appflowy.io'; - // This is a link [appflowy.io](appflowy.io) - final editor = tester.editor - ..insertTextNode( - null, - delta: Delta() - ..insert( - link, - attributes: { - BuiltInAttributeKey.href: link, - }, - ), - ); - await editor.startTesting(); - await tester.pumpAndSettle(); - final finder = find.text(link, findRichText: true); - expect(finder, findsOneWidget); - - // tap the link - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0, endOffset: link.length), - ); - await tester.tap(finder); - await tester.pumpAndSettle(const Duration(milliseconds: 350)); - final linkMenu = find.byType(LinkMenu); - expect(linkMenu, findsOneWidget); - expect(find.text(link, findRichText: true), findsNWidgets(2)); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart deleted file mode 100644 index b3e22f89e6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart +++ /dev/null @@ -1,128 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('checkbox_text_handler.dart', () { - testWidgets('Click checkbox icon', (tester) async { - // Before - // - // [BIUS]Welcome to Appflowy 😁[BIUS] - // - // After - // - // [checkbox]Welcome to Appflowy 😁 - // - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - delta: Delta(operations: [ - TextInsert(text, attributes: { - BuiltInAttributeKey.bold: true, - BuiltInAttributeKey.italic: true, - BuiltInAttributeKey.underline: true, - BuiltInAttributeKey.strikethrough: true, - }), - ]), - ); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - - final selection = - Selection.single(path: [0], startOffset: 0, endOffset: text.length); - var node = editor.nodeAtPath([0]) as TextNode; - var state = node.key.currentState as DefaultSelectable; - var checkboxWidget = find.byKey(state.iconKey!); - await tester.tap(checkboxWidget); - await tester.pumpAndSettle(); - - expect(node.attributes.check, true); - - expect(node.allSatisfyBoldInSelection(selection), true); - expect(node.allSatisfyItalicInSelection(selection), true); - expect(node.allSatisfyUnderlineInSelection(selection), true); - expect(node.allSatisfyStrikethroughInSelection(selection), true); - - node = editor.nodeAtPath([0]) as TextNode; - state = node.key.currentState as DefaultSelectable; - await tester.ensureVisible(find.byKey(state.iconKey!)); - await tester.tap(find.byKey(state.iconKey!)); - await tester.pump(); - - expect(node.attributes.check, false); - expect(node.allSatisfyBoldInSelection(selection), true); - expect(node.allSatisfyItalicInSelection(selection), true); - expect(node.allSatisfyUnderlineInSelection(selection), true); - expect(node.allSatisfyStrikethroughInSelection(selection), true); - }); - - // https://github.com/AppFlowy-IO/AppFlowy/issues/1763 - // // [Bug] Mouse unable to click a certain area #1763 - testWidgets('insert a new checkbox after an existing checkbox', - (tester) async { - // Before - // - // [checkbox] Welcome to Appflowy 😁 - // - // After - // - // [checkbox] Welcome to Appflowy 😁 - // - // [checkbox] Welcome to Appflowy 😁 - // - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - delta: Delta( - operations: [TextInsert(text)], - ), - ); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - - await editor.pressLogicKey(key: LogicalKeyboardKey.enter); - await editor.pressLogicKey(key: LogicalKeyboardKey.enter); - await editor.pressLogicKey(key: LogicalKeyboardKey.enter); - - expect( - editor.documentSelection, - Selection.single(path: [2], startOffset: 0), - ); - - await editor.pressLogicKey(key: LogicalKeyboardKey.slash); - await tester.pumpAndSettle(const Duration(milliseconds: 1000)); - - expect( - find.byType(SelectionMenuWidget, skipOffstage: false), - findsOneWidget, - ); - - final checkboxMenuItem = find.text('Checkbox', findRichText: true); - await tester.tap(checkboxMenuItem); - await tester.pumpAndSettle(); - - final checkboxNode = editor.nodeAtPath([2]) as TextNode; - expect(checkboxNode.subtype, BuiltInAttributeKey.checkbox); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart deleted file mode 100644 index f0710223c1..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart +++ /dev/null @@ -1,417 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - const singleLineText = "One Line Of Text"; - - group('toolbar, heading', (() { - testWidgets('Select Text, Click toolbar and set style for h1 heading', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final h1 = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(h1); - - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - - final h1Button = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.h1'; - } - return false; - }); - - expect(h1Button, findsOneWidget); - await tester.tap(h1Button); - await tester.pumpAndSettle(); - - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.attributes.heading, 'h1'); - }); - - testWidgets('Select Text, Click toolbar and set style for h2 heading', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final h2 = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(h2); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - - final h2Button = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.h2'; - } - return false; - }); - expect(h2Button, findsOneWidget); - await tester.tap(h2Button); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.attributes.heading, 'h2'); - }); - - testWidgets('Select Text, Click toolbar and set style for h3 heading', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final h3 = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(h3); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - - final h3Button = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.h3'; - } - return false; - }); - expect(h3Button, findsOneWidget); - await tester.tap(h3Button); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.attributes.heading, 'h3'); - }); - })); - - group('toolbar, underline', (() { - testWidgets('Select text, click toolbar and set style for underline', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final underline = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(underline); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final underlineButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.underline'; - } - return false; - }); - - expect(underlineButton, findsOneWidget); - await tester.tap(underlineButton); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - // expect(node.attributes.underline, true); - expect(node.allSatisfyUnderlineInSelection(underline), true); - }); - })); - - group('toolbar, bold', (() { - testWidgets('Select Text, Click Toolbar and set style for bold', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final bold = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(bold); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final boldButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.bold'; - } - return false; - }); - - expect(boldButton, findsOneWidget); - await tester.tap(boldButton); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.allSatisfyBoldInSelection(bold), true); - }); - })); - - group('toolbar, italic', (() { - testWidgets('Select Text, Click Toolbar and set style for italic', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final italic = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(italic); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final italicButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.italic'; - } - return false; - }); - - expect(italicButton, findsOneWidget); - await tester.tap(italicButton); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.allSatisfyItalicInSelection(italic), true); - }); - })); - - group('toolbar, strikethrough', (() { - testWidgets('Select Text, Click Toolbar and set style for strikethrough', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final strikeThrough = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(strikeThrough); - - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final strikeThroughButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.strikethrough'; - } - return false; - }); - - expect(strikeThroughButton, findsOneWidget); - await tester.tap(strikeThroughButton); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.allSatisfyStrikethroughInSelection(strikeThrough), true); - }); - })); - - group('toolbar, code', (() { - testWidgets('Select Text, Click Toolbar and set style for code', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final code = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(code); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final codeButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.code'; - } - return false; - }); - - expect(codeButton, findsOneWidget); - await tester.tap(codeButton); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect( - node.allSatisfyInSelection( - code, - BuiltInAttributeKey.code, - (value) { - return value == true; - }, - ), - true, - ); - }); - })); - - group('toolbar, quote', (() { - testWidgets('Select Text, Click Toolbar and set style for quote', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final quote = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(quote); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final quoteButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.quote'; - } - return false; - }); - expect(quoteButton, findsOneWidget); - await tester.tap(quoteButton); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.subtype, 'quote'); - }); - })); - - group('toolbar, bullet list', (() { - testWidgets('Select Text, Click Toolbar and set style for bullet', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final bulletList = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(bulletList); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final bulletListButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.bulleted_list'; - } - return false; - }); - - expect(bulletListButton, findsOneWidget); - await tester.tap(bulletListButton); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.subtype, 'bulleted-list'); - }); - })); - - group('toolbar, number list' , (() { - testWidgets('Select Text, Click Toolbar and set style for number list', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final numberList = Selection( - start: Position(path: [0],offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(numberList); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final numberListButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.number_list'; - } - return false; - }); - expect(numberListButton, findsOneWidget); - await tester.tap(numberListButton); - await tester.pumpAndSettle(); - final node = editor.nodeAtPath([0]) as TextNode; - expect(node.subtype, 'number-list'); - }); - })); - - group('toolbar, highlight', (() { - testWidgets('Select Text, Click Toolbar and set style for highlighted text', - (tester) async { - // FIXME: Use a const value instead of the magic string. - const blue = '0x6000BCF0'; - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final node = editor.nodeAtPath([0]) as TextNode; - final selection = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length)); - - await editor.updateSelection(selection); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final highlightButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.highlight'; - } - return false; - }); - expect(highlightButton, findsOneWidget); - await tester.tap(highlightButton); - await tester.pumpAndSettle(); - expect( - node.allSatisfyInSelection( - selection, - BuiltInAttributeKey.backgroundColor, - (value) { - return value == blue; - }, - ), - true, - ); - }); - })); - - group('toolbar, color picker', (() { - testWidgets( - 'Select Text, Click Toolbar and set color for the selected text', - (tester) async { - final editor = tester.editor..insertTextNode(singleLineText); - await editor.startTesting(); - - final node = editor.nodeAtPath([0]) as TextNode; - final selection = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [0], offset: singleLineText.length), - ); - - await editor.updateSelection(selection); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - final colorButton = find.byWidgetPredicate((widget) { - if (widget is ToolbarItemWidget) { - return widget.item.id == 'appflowy.toolbar.color'; - } - return false; - }); - expect(colorButton, findsOneWidget); - await tester.tap(colorButton); - await tester.pumpAndSettle(); - // select a yellow color - final yellowButton = find.text('Yellow'); - await tester.tap(yellowButton); - await tester.pumpAndSettle(); - expect( - node.allSatisfyInSelection( - selection, - BuiltInAttributeKey.color, - (value) { - return value == Colors.yellow.toHex(); - }, - ), - true, - ); - }); - })); -} - -extension on Color { - String toHex() { - return '0x${value.toRadixString(16)}'; - } -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart deleted file mode 100644 index 188663665e..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart +++ /dev/null @@ -1,259 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('selection_menu_widget.dart', () { - // const i = defaultSelectionMenuItems.length; - // - // Because the `defaultSelectionMenuItems` uses localization, - // and the MaterialApp has not been initialized at the time of getting the value, - // it will crash. - // - // Use const value temporarily instead. - const i = 7; - testWidgets('Selects number.$i item in selection menu with keyboard', - (tester) async { - final editor = await _prepare(tester); - for (var j = 0; j < i; j++) { - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowDown); - } - - await editor.pressLogicKey(key: LogicalKeyboardKey.enter); - expect( - find.byType(SelectionMenuWidget, skipOffstage: false), - findsNothing, - ); - if (defaultSelectionMenuItems[i].name != 'Image') { - await _testDefaultSelectionMenuItems(i, editor); - } - }); - - testWidgets('Selects number.$i item in selection menu with clicking', - (tester) async { - final editor = await _prepare(tester); - await tester.tap(find.byType(SelectionMenuItemWidget).at(i)); - await tester.pumpAndSettle(); - expect( - find.byType(SelectionMenuWidget, skipOffstage: false), - findsNothing, - ); - if (defaultSelectionMenuItems[i].name != 'Image') { - await _testDefaultSelectionMenuItems(i, editor); - } - }); - - testWidgets('Search item in selection menu util no results', - (tester) async { - final editor = await _prepare(tester); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyE); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(3), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(5), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyE); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(3), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyX); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(1), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(1), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNothing, - ); - }); - - testWidgets('Search item in selection menu and presses esc', - (tester) async { - final editor = await _prepare(tester); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyE); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(3), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.escape); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNothing, - ); - }); - - testWidgets('Search item in selection menu and presses backspace', - (tester) async { - final editor = await _prepare(tester); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyT); - await editor.pressLogicKey(key: LogicalKeyboardKey.keyE); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(3), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNothing, - ); - }); - - group('tab and arrow keys move selection in desired direction', () { - testWidgets('left and right keys move selection in desired direction', - (tester) async { - final editor = await _prepare(tester); - - var initialSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowRight); - - var newSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(newSelection.item), 5); - - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowLeft); - - var finalSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - }); - - testWidgets('up and down keys move selection in desired direction', - (tester) async { - final editor = await _prepare(tester); - - var initialSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowDown); - - var newSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(newSelection.item), 1); - - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowUp); - - var finalSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(finalSelection.item), 0); - }); - - testWidgets('arrow keys and tab move same selection', (tester) async { - final editor = await _prepare(tester); - - var initialSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowDown); - - var newSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(newSelection.item), 1); - - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - var finalSelection = getSelectedMenuItem(tester); - expect(defaultSelectionMenuItems.indexOf(finalSelection.item), 6); - }); - - testWidgets( - 'tab moves selection to next row Item on reaching end of current row', - (tester) async { - final editor = await _prepare(tester); - - final initialSelection = getSelectedMenuItem(tester); - - expect(defaultSelectionMenuItems.indexOf(initialSelection.item), 0); - - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - final finalSelection = getSelectedMenuItem(tester); - - expect(defaultSelectionMenuItems.indexOf(finalSelection.item), 1); - }); - }); - }); -} - -Future _prepare(WidgetTester tester) async { - const text = 'Welcome to Appflowy 😁'; - const lines = 3; - final editor = tester.editor; - for (var i = 0; i < lines; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [1], startOffset: 0)); - await editor.pressLogicKey(key: LogicalKeyboardKey.slash); - - await tester.pumpAndSettle(const Duration(milliseconds: 1000)); - - expect( - find.byType(SelectionMenuWidget, skipOffstage: false), - findsOneWidget, - ); - - for (final item in defaultSelectionMenuItems) { - expect(find.text(item.name), findsOneWidget); - } - - return Future.value(editor); -} - -Future _testDefaultSelectionMenuItems( - int index, EditorWidgetTester editor) async { - expect(editor.documentLength, 4); - expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0)); - expect((editor.nodeAtPath([0]) as TextNode).toPlainText(), - 'Welcome to Appflowy 😁'); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), - 'Welcome to Appflowy 😁'); - final node = editor.nodeAtPath([2]); - final item = defaultSelectionMenuItems[index]; - if (item.name == 'Text') { - expect(node?.subtype == null, true); - expect(node?.toString(), null); - } else if (item.name == 'Heading 1') { - expect(node?.subtype, BuiltInAttributeKey.heading); - expect(node?.attributes.heading, BuiltInAttributeKey.h1); - expect(node?.toString(), null); - } else if (item.name == 'Heading 2') { - expect(node?.subtype, BuiltInAttributeKey.heading); - expect(node?.attributes.heading, BuiltInAttributeKey.h2); - expect(node?.toString(), null); - } else if (item.name == 'Heading 3') { - expect(node?.subtype, BuiltInAttributeKey.heading); - expect(node?.attributes.heading, BuiltInAttributeKey.h3); - expect(node?.toString(), null); - } else if (item.name == 'Bulleted list') { - expect(node?.subtype, BuiltInAttributeKey.bulletedList); - } else if (item.name == 'Checkbox') { - expect(node?.subtype, BuiltInAttributeKey.checkbox); - expect(node?.attributes.check, false); - } -} - -SelectionMenuItemWidget getSelectedMenuItem(WidgetTester tester) { - return tester - .state(find.byWidgetPredicate( - (widget) => widget is SelectionMenuItemWidget && widget.isSelected, - )) - .widget as SelectionMenuItemWidget; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/toolbar/toolbar_item_widget_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/toolbar/toolbar_item_widget_test.dart deleted file mode 100644 index 3d212691cb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/toolbar/toolbar_item_widget_test.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('toolbar_item_widget.dart', () { - testWidgets('test single toolbar item widget', (tester) async { - final key = GlobalKey(); - final iconKey = GlobalKey(); - var hit = false; - final item = ToolbarItem( - id: 'appflowy.toolbar.test', - type: 1, - iconBuilder: (isHighlight) { - return Icon( - key: iconKey, - Icons.abc, - color: isHighlight ? Colors.lightBlue : null, - ); - }, - validator: (editorState) => true, - handler: (editorState, context) {}, - highlightCallback: (editorState) { - return true; - }, - ); - final widget = ToolbarItemWidget( - key: key, - item: item, - isHighlight: true, - onPressed: (() { - hit = true; - }), - ); - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: widget, - ), - ), - ); - - expect(find.byKey(key), findsOneWidget); - expect(find.byKey(iconKey), findsOneWidget); - expect( - (tester.firstWidget(find.byKey(iconKey)) as Icon).color, - Colors.lightBlue, - ); - - await tester.tap(find.byKey(key)); - await tester.pumpAndSettle(); - - expect(hit, true); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/toolbar/toolbar_widget_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/render/toolbar/toolbar_widget_test.dart deleted file mode 100644 index d7e6b906f8..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/render/toolbar/toolbar_widget_test.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('toolbar_widget.dart', () { - testWidgets('test toolbar widget', (tester) async {}); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/default_text_operations/format_rich_text_style_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/default_text_operations/format_rich_text_style_test.dart deleted file mode 100644 index ccece41022..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/default_text_operations/format_rich_text_style_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/rich_text/quoted_text.dart'; -import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('format_rich_text_style.dart', () { - testWidgets('formatTextNodes', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0, endOffset: text.length), - ); - - // format the text to Quote - formatTextNodes(editor.editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }); - await tester.pumpAndSettle(const Duration(seconds: 1)); - expect(find.byType(QuotedTextNodeWidget), findsOneWidget); - - // format the text to Quote again. The style should be removed. - formatTextNodes(editor.editorState, { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, - }); - await tester.pumpAndSettle(); - expect(find.byType(QuotedTextNodeWidget), findsNothing); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart deleted file mode 100644 index 75b6696d29..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart +++ /dev/null @@ -1,720 +0,0 @@ -import 'dart:io'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('arrow_keys_handler.dart', () { - testWidgets('Presses arrow right key, move the cursor from left to right', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowRight); - - if (i == text.length - 1) { - // Wrap to next node if the cursor is at the end of the current node. - expect( - editor.documentSelection, - Selection.single( - path: [1], - startOffset: 0, - ), - ); - } else { - expect( - editor.documentSelection, - Selection.single( - path: [0], - startOffset: textNode.delta.nextRunePosition(i), - ), - ); - } - } - }); - }); - - testWidgets( - 'Presses arrow left/right key since selection is not collapsed and backward', - (tester) async { - await _testPressArrowKeyInNotCollapsedSelection(tester, true); - }); - - testWidgets( - 'Presses arrow left/right key since selection is not collapsed and forward', - (tester) async { - await _testPressArrowKeyInNotCollapsedSelection(tester, false); - }); - - testWidgets('Presses arrow left/right + shift in collapsed selection', - (tester) async { - const text = 'Welcome to Appflowy'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - const offset = 8; - final selection = Selection.single(path: [1], startOffset: offset); - await editor.updateSelection(selection); - for (var i = offset - 1; i >= 0; i--) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - ); - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [1], offset: i), - ), - ); - } - for (var i = text.length; i >= 0; i--) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - ); - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: i), - ), - ); - } - for (var i = 1; i <= text.length; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - ); - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: i), - ), - ); - } - for (var i = 0; i < text.length; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - ); - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [1], offset: i), - ), - ); - } - }); - - testWidgets( - 'Presses arrow left/right + shift in not collapsed and backward selection', - (tester) async { - const text = 'Welcome to Appflowy'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - const start = 8; - const end = 12; - final selection = Selection.single( - path: [0], - startOffset: start, - endOffset: end, - ); - await editor.updateSelection(selection); - for (var i = end + 1; i <= text.length; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - ); - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: i), - ), - ); - } - for (var i = text.length - 1; i >= 0; i--) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - ); - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: i), - ), - ); - } - }); - - testWidgets( - 'Presses arrow left/right + command in not collapsed and forward selection', - (tester) async { - const text = 'Welcome to Appflowy'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - const start = 12; - const end = 8; - final selection = Selection.single( - path: [0], - startOffset: start, - endOffset: end, - ); - await editor.updateSelection(selection); - for (var i = end - 1; i >= 0; i--) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - ); - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: i), - ), - ); - } - for (var i = 1; i <= text.length; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - ); - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: i), - ), - ); - } - }); - - testWidgets('Presses arrow left/right/up/down + meta in collapsed selection', - (tester) async { - await _testPressArrowKeyWithMetaInSelection(tester, true, false); - }); - - testWidgets( - 'Presses arrow left/right/up/down + meta in not collapsed and backward selection', - (tester) async { - await _testPressArrowKeyWithMetaInSelection(tester, false, true); - }); - - testWidgets( - 'Presses arrow left/right/up/down + meta in not collapsed and forward selection', - (tester) async { - await _testPressArrowKeyWithMetaInSelection(tester, false, false); - }); - - testWidgets('Presses arrow up/down + shift in not collapsed selection', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(null) - ..insertTextNode(text) - ..insertTextNode(null) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - final selection = Selection.single(path: [3], startOffset: 8); - await editor.updateSelection(selection); - for (int i = 0; i < 3; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowUp, - isShiftPressed: true, - ); - } - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: 0), - ), - ); - for (int i = 0; i < 7; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowDown, - isShiftPressed: true, - ); - } - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [6], offset: 0), - ), - ); - for (int i = 0; i < 3; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowUp, - isShiftPressed: true, - ); - } - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [3], offset: 0), - ), - ); - }); - - testWidgets('Presses shift + arrow down and meta/ctrl + shift + right', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - final selection = Selection.single(path: [0], startOffset: 8); - await editor.updateSelection(selection); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowDown, - isShiftPressed: true, - ); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - isMetaPressed: true, - ); - } - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [1], offset: text.length), - ), - ); - }); - - testWidgets('Presses shift + arrow up and meta/ctrl + shift + left', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - final selection = Selection.single(path: [1], startOffset: 8); - await editor.updateSelection(selection); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowUp, - isShiftPressed: true, - ); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - isMetaPressed: true, - ); - } - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: 0), - ), - ); - }); - - testWidgets('Presses shift + alt + arrow left to select a word', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - final selection = Selection.single(path: [1], startOffset: 10); - await editor.updateSelection(selection); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - isAltPressed: true, - ); - // - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [1], offset: 8), - ), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - isAltPressed: true, - ); - // < to> - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [1], offset: 7), - ), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - isAltPressed: true, - ); - // - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [1], offset: 0), - ), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isShiftPressed: true, - isAltPressed: true, - ); - // <😁> - // - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: 22), - ), - ); - }); - - testWidgets('Presses shift + alt + arrow right to select a word', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - final selection = Selection.single(path: [0], startOffset: 10); - await editor.updateSelection(selection); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - isAltPressed: true, - ); - // < > - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: 11), - ), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - isAltPressed: true, - ); - // < Appflowy> - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: 19), - ), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - isAltPressed: true, - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - isAltPressed: true, - ); - // < Appflowy 😁> - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [0], offset: 22), - ), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isShiftPressed: true, - isAltPressed: true, - ); - // < Appflowy 😁> - // <> - expect( - editor.documentSelection, - selection.copyWith( - end: Position(path: [1], offset: 0), - ), - ); - }); - - testWidgets('Presses ctrl + backspace to delete a word', (tester) async { - List words = ["Welcome", " ", "to", " ", "Appflowy", " ", "😁"]; - final text = words.join(); - final editor = tester.editor..insertTextNode(text); - - await editor.startTesting(); - var selection = Selection.single(path: [0], startOffset: text.length); - await editor.updateSelection(selection); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isMetaPressed: true, - ); - } - - //fetching all the text that is still on the editor. - var nodes = - editor.editorState.service.selectionService.currentSelectedNodes; - var textNode = nodes.whereType().first; - var newText = textNode.toPlainText(); - - words.removeLast(); - expect(newText, words.join()); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isMetaPressed: true, - ); - } - - //fetching all the text that is still on the editor. - nodes = editor.editorState.service.selectionService.currentSelectedNodes; - textNode = nodes.whereType().first; - - newText = textNode.toPlainText(); - - words.removeLast(); - expect(newText, words.join()); - - for (var i = 0; i < words.length; i++) { - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isMetaPressed: true, - ); - } - } - - nodes = editor.editorState.service.selectionService.currentSelectedNodes; - textNode = nodes.whereType().toList(growable: false).first; - - newText = textNode.toPlainText(); - - expect(newText, ''); - }); - - testWidgets('Testing ctrl + backspace edge cases', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor..insertTextNode(text); - - await editor.startTesting(); - var selection = Selection.single(path: [0], startOffset: 0); - await editor.updateSelection(selection); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isMetaPressed: true, - ); - } - - //fetching all the text that is still on the editor. - var nodes = - editor.editorState.service.selectionService.currentSelectedNodes; - var textNode = nodes.whereType().first; - var newText = textNode.toPlainText(); - - //nothing happens - expect(newText, text); - - selection = Selection.single(path: [0], startOffset: 14); - await editor.updateSelection(selection); - //Welcome to App|flowy 😁 - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - isMetaPressed: true, - ); - } - - //fetching all the text that is still on the editor. - nodes = editor.editorState.service.selectionService.currentSelectedNodes; - textNode = nodes.whereType().first; - newText = textNode.toPlainText(); - - const expectedText = 'Welcome to flowy 😁'; - expect(newText, expectedText); - }); -} - -Future _testPressArrowKeyInNotCollapsedSelection( - WidgetTester tester, bool isBackward) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - final start = Position(path: [0], offset: 5); - final end = Position(path: [1], offset: 10); - final selection = Selection( - start: isBackward ? start : end, - end: isBackward ? end : start, - ); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowLeft); - expect(editor.documentSelection?.start, start); - - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.arrowRight); - expect(editor.documentSelection?.end, end); -} - -Future _testPressArrowKeyWithMetaInSelection( - WidgetTester tester, - bool isSingle, - bool isBackward, -) async { - const text = 'Welcome to Appflowy'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - Selection selection; - if (isSingle) { - selection = Selection.single( - path: [0], - startOffset: 8, - ); - } else { - if (isBackward) { - selection = Selection.single( - path: [0], - startOffset: 8, - endOffset: text.length, - ); - } else { - selection = Selection.single( - path: [0], - startOffset: text.length, - endOffset: 8, - ); - } - } - await editor.updateSelection(selection); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isMetaPressed: true, - ); - } - - expect( - editor.documentSelection, - Selection.single(path: [0], startOffset: 0), - ); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isMetaPressed: true, - ); - } - - expect( - editor.documentSelection, - Selection.single(path: [0], startOffset: text.length), - ); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowUp, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowUp, - isMetaPressed: true, - ); - } - - expect( - editor.documentSelection, - Selection.single(path: [0], startOffset: 0), - ); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowDown, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowDown, - isMetaPressed: true, - ); - } - - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: text.length), - ); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart deleted file mode 100644 index b299c18aa4..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart +++ /dev/null @@ -1,642 +0,0 @@ -import 'dart:collection'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/image/image_node_widget.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:network_image_mock/network_image_mock.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('backspace_handler.dart', () { - testWidgets('Presses backspace key in empty document', (tester) async { - // Before - // - // [Empty Line] - // - // After - // - // [Empty Line] - // - final editor = tester.editor..insertEmptyTextNode(); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - // Pressing the backspace key continuously. - for (int i = 1; i <= 1; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - ); - expect(editor.documentLength, 1); - expect(editor.documentSelection, - Selection.single(path: [0], startOffset: 0)); - } - }); - }); - - // Before - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - // After - // - // Welcome to Appflowy 😁 - // Welcome t Appflowy 😁 - // Welcome Appflowy 😁 - // - // Then - // Welcome to Appflowy 😁 - // - testWidgets( - 'Presses backspace key in non-empty document and selection is backward', - (tester) async { - await _deleteTextByBackspace(tester, true); - }); - testWidgets( - 'Presses backspace key in non-empty document and selection is forward', - (tester) async { - await _deleteTextByBackspace(tester, false); - }); - - // Before - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - // After - // - // Welcome to Appflowy 😁 - // Welcome t Appflowy 😁 - // Welcome Appflowy 😁 - // - // Then - // Welcome to Appflowy 😁 - // - testWidgets( - 'Presses delete key in non-empty document and selection is backward', - (tester) async { - await _deleteTextByDelete(tester, true); - }); - testWidgets( - 'Presses delete key in non-empty document and selection is forward', - (tester) async { - await _deleteTextByDelete(tester, false); - }); - - // Before - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - // After - // - // Welcome to Appflowy 😁Welcome Appflowy 😁 - testWidgets( - 'Presses delete key in non-empty document and selection is at the end of the text', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - // delete 'o' - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.delete); - - expect(editor.documentLength, 1); - expect(editor.documentSelection, - Selection.single(path: [0], startOffset: text.length)); - expect((editor.nodeAtPath([0]) as TextNode).toPlainText(), text * 2); - }); - - // Before - // - // Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁 - // - // After - // - // Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁Welcome to Appflowy 😁 - // - testWidgets('Presses backspace key in styled text (checkbox)', - (tester) async { - await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.checkbox); - }); - testWidgets('Presses backspace key in styled text (bulletedList)', - (tester) async { - await _deleteStyledTextByBackspace( - tester, BuiltInAttributeKey.bulletedList); - }); - testWidgets('Presses backspace key in styled text (heading)', (tester) async { - await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.heading); - }); - testWidgets('Presses backspace key in styled text (quote)', (tester) async { - await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.quote); - }); - - // Before - // - // Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁 - // - // After - // - // Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁 - // - testWidgets('Presses delete key in styled text (checkbox)', (tester) async { - await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.checkbox); - }); - testWidgets('Presses delete key in styled text (bulletedList)', - (tester) async { - await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.bulletedList); - }); - testWidgets('Presses delete key in styled text (heading)', (tester) async { - await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.heading); - }); - testWidgets('Presses delete key in styled text (quote)', (tester) async { - await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.quote); - }); - - // Before - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // [Image] - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - // After - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - testWidgets('Deletes the image surrounded by text', (tester) async { - mockNetworkImagesFor(() async { - const text = 'Welcome to Appflowy 😁'; - const src = 'https://s1.ax1x.com/2022/08/26/v2sSbR.jpg'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertImageNode(src) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - expect(editor.documentLength, 5); - expect(find.byType(ImageNodeWidget), findsOneWidget); - - await editor.updateSelection( - Selection( - start: Position(path: [1], offset: 0), - end: Position(path: [3], offset: text.length), - ), - ); - - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.documentLength, 3); - expect(find.byType(ImageNodeWidget), findsNothing); - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 0), - ); - }); - }); - - testWidgets('Deletes the first image, and selection is backward', - (tester) async { - await _deleteFirstImage(tester, true); - }); - - testWidgets('Deletes the first image, and selection is not backward', - (tester) async { - await _deleteFirstImage(tester, false); - }); - - testWidgets('Deletes the last image and selection is backward', - (tester) async { - await _deleteLastImage(tester, true); - }); - - testWidgets('Deletes the last image and selection is not backward', - (tester) async { - await _deleteLastImage(tester, false); - }); - - testWidgets('Removes the style of heading text and revert', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - - final textNode = editor.nodeAtPath([0]) as TextNode; - - await editor.insertText(textNode, '#', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect( - (editor.nodeAtPath([0]) as TextNode).attributes.heading, - BuiltInAttributeKey.h1, - ); - - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect( - textNode.attributes.heading, - null, - ); - - await editor.insertText(textNode, '#', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect( - (editor.nodeAtPath([0]) as TextNode).attributes.heading, - BuiltInAttributeKey.h1, - ); - }); - - testWidgets('Delete the nested bulleted list', (tester) async { - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - const text = 'Welcome to Appflowy 😁'; - final node = TextNode( - delta: Delta()..insert(text), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }, - ); - node.insert( - node.copyWith() - ..insert( - node.copyWith(), - ), - ); - - final editor = tester.editor..insert(node); - await editor.startTesting(); - - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - await editor.updateSelection( - Selection.single(path: [0, 0, 0], startOffset: 0), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.nodeAtPath([0, 0, 0])?.subtype, null); - await editor.updateSelection( - Selection.single(path: [0, 0, 0], startOffset: 0), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.nodeAtPath([0, 1]) != null, true); - await editor.updateSelection( - Selection.single(path: [0, 1], startOffset: 0), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.nodeAtPath([1]) != null, true); - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0), - ); - - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁Welcome to Appflowy 😁 - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect( - editor.documentSelection, - Selection.single(path: [0, 0], startOffset: text.length), - ); - expect((editor.nodeAtPath([0, 0]) as TextNode).toPlainText(), text * 2); - }); - - testWidgets('Delete the complicated nested bulleted list', (tester) async { - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - const text = 'Welcome to Appflowy 😁'; - final node = TextNode( - delta: Delta()..insert(text), - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, - }, - ); - - node - ..insert( - node.copyWith(children: LinkedList()), - ) - ..insert( - node.copyWith(children: LinkedList()) - ..insert( - node.copyWith(children: LinkedList()), - ) - ..insert( - node.copyWith(children: LinkedList()), - ), - ); - - final editor = tester.editor..insert(node); - await editor.startTesting(); - - await editor.updateSelection( - Selection.single(path: [0, 1], startOffset: 0), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect( - editor.nodeAtPath([0, 1])!.subtype != BuiltInAttributeKey.bulletedList, - true, - ); - expect( - editor.nodeAtPath([0, 1, 0])!.subtype, - BuiltInAttributeKey.bulletedList, - ); - expect( - editor.nodeAtPath([0, 1, 1])!.subtype, - BuiltInAttributeKey.bulletedList, - ); - expect(find.byType(FlowyRichText), findsNWidgets(5)); - - // Before - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // After - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect( - editor.nodeAtPath([0, 0])!.subtype == BuiltInAttributeKey.bulletedList, - true, - ); - expect( - (editor.nodeAtPath([0, 0]) as TextNode).toPlainText() == text * 2, - true, - ); - expect( - editor.nodeAtPath([0, 1])!.subtype == BuiltInAttributeKey.bulletedList, - true, - ); - expect( - editor.nodeAtPath([0, 2])!.subtype == BuiltInAttributeKey.bulletedList, - true, - ); - }); -} - -Future _deleteFirstImage(WidgetTester tester, bool isBackward) async { - mockNetworkImagesFor(() async { - const text = 'Welcome to Appflowy 😁'; - const src = 'https://s1.ax1x.com/2022/08/26/v2sSbR.jpg'; - final editor = tester.editor - ..insertImageNode(src) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - expect(editor.documentLength, 3); - expect(find.byType(ImageNodeWidget), findsOneWidget); - - final start = Position(path: [0], offset: 0); - final end = Position(path: [1], offset: 1); - await editor.updateSelection( - Selection( - start: isBackward ? start : end, - end: isBackward ? end : start, - ), - ); - - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.documentLength, 2); - expect(find.byType(ImageNodeWidget), findsNothing); - expect(editor.documentSelection, Selection.collapsed(start)); - }); -} - -Future _deleteLastImage(WidgetTester tester, bool isBackward) async { - mockNetworkImagesFor(() async { - const text = 'Welcome to Appflowy 😁'; - const src = 'https://s1.ax1x.com/2022/08/26/v2sSbR.jpg'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertImageNode(src); - await editor.startTesting(); - - expect(editor.documentLength, 3); - expect(find.byType(ImageNodeWidget), findsOneWidget); - - final start = Position(path: [1], offset: 0); - final end = Position(path: [2], offset: 1); - await editor.updateSelection( - Selection( - start: isBackward ? start : end, - end: isBackward ? end : start, - ), - ); - - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.documentLength, 2); - expect(find.byType(ImageNodeWidget), findsNothing); - expect(editor.documentSelection, Selection.collapsed(start)); - }); -} - -Future _deleteStyledTextByBackspace( - WidgetTester tester, String style) async { - const text = 'Welcome to Appflowy 😁'; - Attributes attributes = { - BuiltInAttributeKey.subtype: style, - }; - if (style == BuiltInAttributeKey.checkbox) { - attributes[BuiltInAttributeKey.checkbox] = true; - } else if (style == BuiltInAttributeKey.numberList) { - attributes[BuiltInAttributeKey.number] = 1; - } else if (style == BuiltInAttributeKey.heading) { - attributes[BuiltInAttributeKey.heading] = BuiltInAttributeKey.h1; - } - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text, attributes: attributes) - ..insertTextNode(text, attributes: attributes); - - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [2], startOffset: 0), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - ); - expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0)); - - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - ); - expect(editor.documentLength, 2); - expect(editor.documentSelection, - Selection.single(path: [1], startOffset: text.length)); - expect(editor.nodeAtPath([1])?.subtype, style); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text * 2); - - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.backspace, - ); - expect(editor.documentLength, 2); - expect(editor.documentSelection, Selection.single(path: [1], startOffset: 0)); - expect(editor.nodeAtPath([1])?.subtype, null); -} - -Future _deleteStyledTextByDelete( - WidgetTester tester, String style) async { - const text = 'Welcome to Appflowy 😁'; - Attributes attributes = { - BuiltInAttributeKey.subtype: style, - }; - if (style == BuiltInAttributeKey.checkbox) { - attributes[BuiltInAttributeKey.checkbox] = true; - } else if (style == BuiltInAttributeKey.numberList) { - attributes[BuiltInAttributeKey.number] = 1; - } else if (style == BuiltInAttributeKey.heading) { - attributes[BuiltInAttributeKey.heading] = BuiltInAttributeKey.h1; - } - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text, attributes: attributes) - ..insertTextNode(text, attributes: attributes); - - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0), - ); - for (var i = 1; i < text.length; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.delete, - ); - expect( - editor.documentSelection, Selection.single(path: [1], startOffset: 0)); - expect(editor.nodeAtPath([1])?.subtype, style); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), - text.safeSubString(i)); - } - - await editor.pressLogicKey( - key: LogicalKeyboardKey.delete, - ); - expect(editor.documentLength, 2); - expect(editor.documentSelection, Selection.single(path: [1], startOffset: 0)); - expect(editor.nodeAtPath([1])?.subtype, style); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text); -} - -Future _deleteTextByBackspace( - WidgetTester tester, bool isBackwardSelection) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - // delete 'o' - await editor.updateSelection( - Selection.single(path: [1], startOffset: 10), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - - expect(editor.documentLength, 3); - expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9)); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), - 'Welcome t Appflowy 😁'); - - // delete 'to ' - await editor.updateSelection( - Selection.single(path: [2], startOffset: 8, endOffset: 11), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.documentLength, 3); - expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8)); - expect((editor.nodeAtPath([2]) as TextNode).toPlainText(), - 'Welcome Appflowy 😁'); - - // delete 'Appflowy 😁 - // Welcome t Appflowy 😁 - // Welcome ' - final start = Position(path: [0], offset: 11); - final end = Position(path: [2], offset: 8); - await editor.updateSelection(Selection( - start: isBackwardSelection ? start : end, - end: isBackwardSelection ? end : start)); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.documentLength, 1); - expect( - editor.documentSelection, Selection.single(path: [0], startOffset: 11)); - expect((editor.nodeAtPath([0]) as TextNode).toPlainText(), - 'Welcome to Appflowy 😁'); -} - -Future _deleteTextByDelete( - WidgetTester tester, bool isBackwardSelection) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - // delete 'o' - await editor.updateSelection( - Selection.single(path: [1], startOffset: 9), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.delete); - - expect(editor.documentLength, 3); - expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9)); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), - 'Welcome t Appflowy 😁'); - - // delete 'to ' - await editor.updateSelection( - Selection.single(path: [2], startOffset: 8, endOffset: 11), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.delete); - expect(editor.documentLength, 3); - expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8)); - expect((editor.nodeAtPath([2]) as TextNode).toPlainText(), - 'Welcome Appflowy 😁'); - - // delete 'Appflowy 😁 - // Welcome t Appflowy 😁 - // Welcome ' - final start = Position(path: [0], offset: 11); - final end = Position(path: [2], offset: 8); - await editor.updateSelection(Selection( - start: isBackwardSelection ? start : end, - end: isBackwardSelection ? end : start)); - await editor.pressLogicKey(key: LogicalKeyboardKey.delete); - expect(editor.documentLength, 1); - expect( - editor.documentSelection, Selection.single(path: [0], startOffset: 11)); - expect((editor.nodeAtPath([0]) as TextNode).toPlainText(), - 'Welcome to Appflowy 😁'); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/checkbox_event_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/checkbox_event_handler_test.dart deleted file mode 100644 index 03d5c78d04..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/checkbox_event_handler_test.dart +++ /dev/null @@ -1,241 +0,0 @@ -import 'dart:io'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/service/shortcut_event/built_in_shortcut_events.dart'; - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('checkbox_event_handler_test.dart', () { - testWidgets('toggle checkbox with shortcut ctrl+enter', (tester) async { - const text = 'Checkbox1'; - final editor = tester.editor - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - delta: Delta( - operations: [TextInsert(text)], - ), - ); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - - final checkboxNode = editor.nodeAtPath([0]) as TextNode; - expect(checkboxNode.subtype, BuiltInAttributeKey.checkbox); - expect(checkboxNode.attributes[BuiltInAttributeKey.checkbox], false); - - for (final event in builtInShortcutEvents) { - if (event.key == 'Toggle Checkbox') { - event.updateCommand( - windowsCommand: 'ctrl+enter', - linuxCommand: 'ctrl+enter', - macOSCommand: 'meta+enter', - ); - } - } - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - isMetaPressed: true, - ); - } - - expect(checkboxNode.attributes[BuiltInAttributeKey.checkbox], true); - - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - isMetaPressed: true, - ); - } - - expect(checkboxNode.attributes[BuiltInAttributeKey.checkbox], false); - }); - - testWidgets( - 'test if all checkboxes get unchecked after toggling them, if all of them were already checked', - (tester) async { - const text = 'Checkbox'; - final editor = tester.editor - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: true, - }, - delta: Delta( - operations: [TextInsert(text)], - ), - ) - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: true, - }, - delta: Delta( - operations: [TextInsert(text)], - ), - ) - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: true, - }, - delta: Delta( - operations: [TextInsert(text)], - ), - ); - - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - - final nodes = - editor.editorState.service.selectionService.currentSelectedNodes; - final checkboxTextNodes = nodes - .where( - (element) => - element is TextNode && - element.subtype == BuiltInAttributeKey.checkbox, - ) - .toList(growable: false); - - for (final node in checkboxTextNodes) { - expect(node.attributes[BuiltInAttributeKey.checkbox], true); - } - - for (final event in builtInShortcutEvents) { - if (event.key == 'Toggle Checkbox') { - event.updateCommand( - windowsCommand: 'ctrl+enter', - linuxCommand: 'ctrl+enter', - macOSCommand: 'meta+enter', - ); - } - } - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - isMetaPressed: true, - ); - } - - for (final node in checkboxTextNodes) { - expect(node.attributes[BuiltInAttributeKey.checkbox], false); - } - }); - - testWidgets( - 'test if all checkboxes get checked after toggling them, if any one of them were already checked', - (tester) async { - const text = 'Checkbox'; - final editor = tester.editor - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - delta: Delta( - operations: [TextInsert(text)], - ), - ) - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: true, - }, - delta: Delta( - operations: [TextInsert(text)], - ), - ) - ..insertTextNode( - '', - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - delta: Delta( - operations: [TextInsert(text)], - ), - ); - - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - - final nodes = - editor.editorState.service.selectionService.currentSelectedNodes; - final checkboxTextNodes = nodes - .where( - (element) => - element is TextNode && - element.subtype == BuiltInAttributeKey.checkbox, - ) - .toList(growable: false); - - for (final event in builtInShortcutEvents) { - if (event.key == 'Toggle Checkbox') { - event.updateCommand( - windowsCommand: 'ctrl+enter', - linuxCommand: 'ctrl+enter', - macOSCommand: 'meta+enter', - ); - } - } - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - isMetaPressed: true, - ); - } - - for (final node in checkboxTextNodes) { - expect(node.attributes[BuiltInAttributeKey.checkbox], true); - } - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart deleted file mode 100644 index 9bca3748d6..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart +++ /dev/null @@ -1,232 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('enter_without_shift_in_text_node_handler.dart', () { - testWidgets('Presses enter key in empty document', (tester) async { - // Before - // - // [Empty Line] - // - // After - // - // [Empty Line] * 10 - // - final editor = tester.editor..insertEmptyTextNode(); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - // Pressing the enter key continuously. - for (int i = 1; i <= 10; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - ); - expect(editor.documentLength, i + 1); - expect(editor.documentSelection, - Selection.single(path: [i], startOffset: 0)); - } - }); - - testWidgets('Presses enter key in non-empty document', (tester) async { - // Before - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - // After - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // [Empty Line] - // Welcome to Appflowy 😁 - // - const text = 'Welcome to Appflowy 😁'; - var lines = 3; - - final editor = tester.editor; - for (var i = 1; i <= lines; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - - expect(editor.documentLength, lines); - - // Presses the enter key in last line. - await editor.updateSelection( - Selection.single(path: [lines - 1], startOffset: 0), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - ); - lines += 1; - expect(editor.documentLength, lines); - expect(editor.documentSelection, - Selection.single(path: [lines - 1], startOffset: 0)); - var lastNode = editor.nodeAtPath([lines - 1]); - expect(lastNode != null, true); - expect(lastNode is TextNode, true); - lastNode = lastNode as TextNode; - expect(lastNode.delta.toPlainText(), text); - expect((lastNode.previous as TextNode).delta.toPlainText(), ''); - expect( - (lastNode.previous!.previous as TextNode).delta.toPlainText(), text); - }); - - // Before - // - // Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁 - // - // After - // - // Welcome to Appflowy 😁 - // [Empty Line] - // [Style] Welcome to Appflowy 😁 - // [Style] Welcome to Appflowy 😁 - // [Style] - testWidgets('Presses enter key in bulleted list', (tester) async { - await _testStyleNeedToBeCopy(tester, BuiltInAttributeKey.bulletedList); - }); - testWidgets('Presses enter key in numbered list', (tester) async { - await _testStyleNeedToBeCopy(tester, BuiltInAttributeKey.numberList); - }); - testWidgets('Presses enter key in checkbox styled text', (tester) async { - await _testStyleNeedToBeCopy(tester, BuiltInAttributeKey.checkbox); - }); - testWidgets('Presses enter key in quoted text', (tester) async { - await _testStyleNeedToBeCopy(tester, BuiltInAttributeKey.quote); - }); - - testWidgets('Presses enter key in multiple selection from top to bottom', - (tester) async { - _testMultipleSelection(tester, true); - }); - - testWidgets('Presses enter key in multiple selection from bottom to top', - (tester) async { - _testMultipleSelection(tester, false); - }); - - testWidgets('Presses enter key in the first line', (tester) async { - // Before - // - // Welcome to Appflowy 😁 - // - // After - // - // [Empty Line] - // Welcome to Appflowy 😁 - // - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.enter); - expect(editor.documentLength, 2); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text); - }); - }); -} - -Future _testStyleNeedToBeCopy(WidgetTester tester, String style) async { - const text = 'Welcome to Appflowy 😁'; - Attributes attributes = { - BuiltInAttributeKey.subtype: style, - }; - if (style == BuiltInAttributeKey.checkbox) { - attributes[BuiltInAttributeKey.checkbox] = true; - } else if (style == BuiltInAttributeKey.numberList) { - attributes[BuiltInAttributeKey.number] = 1; - } - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text, attributes: attributes) - ..insertTextNode(text, attributes: attributes); - - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - ); - expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0)); - - await editor.updateSelection( - Selection.single(path: [3], startOffset: text.length), - ); - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - ); - expect(editor.documentSelection, Selection.single(path: [4], startOffset: 0)); - - if ([BuiltInAttributeKey.heading, BuiltInAttributeKey.quote] - .contains(style)) { - expect(editor.nodeAtPath([4])?.subtype, null); - - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - ); - expect( - editor.documentSelection, Selection.single(path: [5], startOffset: 0)); - expect(editor.nodeAtPath([5])?.subtype, null); - } else { - expect(editor.nodeAtPath([4])?.subtype, style); - - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - ); - expect( - editor.documentSelection, Selection.single(path: [4], startOffset: 0)); - expect(editor.nodeAtPath([4])?.subtype, null); - } -} - -Future _testMultipleSelection( - WidgetTester tester, bool isBackwardSelection) async { - // Before - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - // After - // - // Welcome - // to Appflowy 😁 - // - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor; - var lines = 4; - - for (var i = 1; i <= lines; i++) { - editor.insertTextNode(text); - } - - await editor.startTesting(); - final start = Position(path: [0], offset: 7); - final end = Position(path: [3], offset: 8); - await editor.updateSelection(Selection( - start: isBackwardSelection ? start : end, - end: isBackwardSelection ? end : start, - )); - await editor.pressLogicKey( - key: LogicalKeyboardKey.enter, - ); - - expect(editor.documentLength, 2); - expect((editor.nodeAtPath([0]) as TextNode).toPlainText(), 'Welcome'); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), 'to Appflowy 😁'); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/exit_editing_mode_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/exit_editing_mode_handler_test.dart deleted file mode 100644 index 0d264995cc..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/exit_editing_mode_handler_test.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('exit_editing_mode_handler.dart', () { - testWidgets('Exit editing mode', (tester) async { - const text = 'Welcome to Appflowy 😁'; - const lines = 3; - final editor = tester.editor; - for (var i = 0; i < lines; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - - // collaspsed selection - await _testSelection(editor, Selection.single(path: [1], startOffset: 0)); - - // single selection - await _testSelection( - editor, - Selection.single(path: [1], startOffset: 0, endOffset: text.length), - ); - - // multiple selection - await _testSelection( - editor, - Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [2], offset: text.length), - ), - ); - }); - - // Future _testSelection() - }); -} - -Future _testSelection( - EditorWidgetTester editor, Selection selection) async { - await editor.updateSelection(selection); - expect(editor.documentSelection, selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.escape); - expect(editor.documentSelection, null); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart deleted file mode 100644 index 842fcc6768..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart +++ /dev/null @@ -1,316 +0,0 @@ -import 'dart:io'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/link_menu/link_menu.dart'; -import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('format_style_handler.dart', () { - testWidgets('Presses Command + B to update text style', (tester) async { - await _testUpdateTextStyleByCommandX( - tester, - BuiltInAttributeKey.bold, - true, - LogicalKeyboardKey.keyB, - ); - }); - testWidgets('Presses Command + I to update text style', (tester) async { - await _testUpdateTextStyleByCommandX( - tester, - BuiltInAttributeKey.italic, - true, - LogicalKeyboardKey.keyI, - ); - }); - testWidgets('Presses Command + U to update text style', (tester) async { - await _testUpdateTextStyleByCommandX( - tester, - BuiltInAttributeKey.underline, - true, - LogicalKeyboardKey.keyU, - ); - }); - testWidgets('Presses Command + Shift + S to update text style', - (tester) async { - await _testUpdateTextStyleByCommandX( - tester, - BuiltInAttributeKey.strikethrough, - true, - LogicalKeyboardKey.keyS, - ); - }); - - testWidgets('Presses Command + Shift + H to update text style', - (tester) async { - // FIXME: customize the highlight color instead of using magic number. - await _testUpdateTextStyleByCommandX( - tester, - BuiltInAttributeKey.backgroundColor, - '0x6000BCF0', - LogicalKeyboardKey.keyH, - ); - }); - - testWidgets('Presses Command + K to trigger link menu', (tester) async { - await _testLinkMenuInSingleTextSelection(tester); - }); - - testWidgets('Presses Command + E to update text style', (tester) async { - await _testUpdateTextStyleByCommandX( - tester, - BuiltInAttributeKey.code, - true, - LogicalKeyboardKey.keyE, - ); - }); - }); -} - -Future _testUpdateTextStyleByCommandX( - WidgetTester tester, - String matchStyle, - dynamic matchValue, - LogicalKeyboardKey key, -) async { - final isShiftPressed = - key == LogicalKeyboardKey.keyS || key == LogicalKeyboardKey.keyH; - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - var selection = - Selection.single(path: [1], startOffset: 2, endOffset: text.length - 2); - await editor.updateSelection(selection); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isMetaPressed: true, - ); - } - var textNode = editor.nodeAtPath([1]) as TextNode; - expect( - textNode.allSatisfyInSelection( - selection, - matchStyle, - (value) { - return value == matchValue; - }, - ), - true); - - selection = - Selection.single(path: [1], startOffset: 0, endOffset: text.length); - await editor.updateSelection(selection); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isMetaPressed: true, - ); - } - textNode = editor.nodeAtPath([1]) as TextNode; - expect( - textNode.allSatisfyInSelection( - selection, - matchStyle, - (value) { - return value == matchValue; - }, - ), - true); - - await editor.updateSelection(selection); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isMetaPressed: true, - ); - } - textNode = editor.nodeAtPath([1]) as TextNode; - expect(textNode.allNotSatisfyInSelection(matchStyle, matchValue, selection), - true); - - selection = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [2], offset: text.length), - ); - await editor.updateSelection(selection); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isMetaPressed: true, - ); - } - var nodes = editor.editorState.service.selectionService.currentSelectedNodes - .whereType(); - expect(nodes.length, 3); - for (final node in nodes) { - expect( - node.allSatisfyInSelection( - Selection.single( - path: node.path, - startOffset: 0, - endOffset: text.length, - ), - matchStyle, - (value) { - return value == matchValue; - }, - ), - true, - ); - } - - await editor.updateSelection(selection); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: key, - isShiftPressed: isShiftPressed, - isMetaPressed: true, - ); - } - nodes = editor.editorState.service.selectionService.currentSelectedNodes - .whereType(); - expect(nodes.length, 3); - for (final node in nodes) { - expect( - node.allNotSatisfyInSelection( - matchStyle, - matchValue, - Selection.single( - path: node.path, startOffset: 0, endOffset: text.length), - ), - true, - ); - } -} - -Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async { - const link = 'appflowy.io'; - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - final selection = - Selection.single(path: [1], startOffset: 0, endOffset: text.length); - await editor.updateSelection(selection); - - // show toolbar - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - - // trigger the link menu - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyK, isControlPressed: true); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyK, isMetaPressed: true); - } - expect(find.byType(LinkMenu), findsOneWidget); - - await tester.enterText(find.byType(TextField), link); - await tester.testTextInput.receiveAction(TextInputAction.done); - await tester.pumpAndSettle(); - - expect(find.byType(LinkMenu), findsNothing); - - final node = editor.nodeAtPath([1]) as TextNode; - expect( - node.allSatisfyInSelection( - selection, - BuiltInAttributeKey.href, - (value) => value == link, - ), - true); - - await editor.updateSelection(selection); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyK, isControlPressed: true); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyK, isMetaPressed: true); - } - expect(find.byType(LinkMenu), findsOneWidget); - expect( - find.text(link, findRichText: true, skipOffstage: false), findsOneWidget); - - // Copy link - final copyLink = find.text('Copy link'); - expect(copyLink, findsOneWidget); - await tester.tap(copyLink); - await tester.pumpAndSettle(); - expect(find.byType(LinkMenu), findsNothing); - - // Remove link - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyK, isControlPressed: true); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyK, isMetaPressed: true); - } - final removeLink = find.text('Remove link'); - expect(removeLink, findsOneWidget); - await tester.tap(removeLink); - await tester.pumpAndSettle(); - expect(find.byType(LinkMenu), findsNothing); - - expect( - node.allSatisfyInSelection( - selection, - BuiltInAttributeKey.href, - (value) => value == link, - ), - false); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart deleted file mode 100644 index 28b27a1c29..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart +++ /dev/null @@ -1,493 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('markdown_syntax_to_styled_text.dart', () { - group('convert single backquote to code', () { - Future insertBackquote( - EditorWidgetTester editor, { - int repeat = 1, - }) async { - for (var i = 0; i < repeat; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backquote, - ); - } - } - - testWidgets('`AppFlowy` to code AppFlowy', (tester) async { - const text = '`AppFlowy'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertBackquote(editor); - final allCode = textNode.allSatisfyCodeInSelection( - Selection.single( - path: [0], - startOffset: 0, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allCode, true); - expect(textNode.toPlainText(), 'AppFlowy'); - }); - - testWidgets('App`Flowy` to code AppFlowy', (tester) async { - const text = 'App`Flowy'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertBackquote(editor); - final allCode = textNode.allSatisfyCodeInSelection( - Selection.single( - path: [0], - startOffset: 3, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allCode, true); - expect(textNode.toPlainText(), 'AppFlowy'); - }); - - testWidgets('`` nothing changes', (tester) async { - const text = '`'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertBackquote(editor); - final allCode = textNode.allSatisfyCodeInSelection( - Selection.single( - path: [0], - startOffset: 0, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allCode, false); - expect(textNode.toPlainText(), text); - }); - }); - - group('convert double backquote to code', () { - Future insertBackquote( - EditorWidgetTester editor, { - int repeat = 1, - }) async { - for (var i = 0; i < repeat; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.backquote, - ); - } - } - - testWidgets('```AppFlowy`` to code `AppFlowy', (tester) async { - const text = '```AppFlowy`'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertBackquote(editor); - final allCode = textNode.allSatisfyCodeInSelection( - Selection.single( - path: [0], - startOffset: 1, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allCode, true); - expect(textNode.toPlainText(), '`AppFlowy'); - }); - - testWidgets('```` nothing changes', (tester) async { - const text = '```'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertBackquote(editor); - final allCode = textNode.allSatisfyCodeInSelection( - Selection.single( - path: [0], - startOffset: 0, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allCode, false); - expect(textNode.toPlainText(), text); - }); - }); - - group('convert double tilde to strikethrough', () { - Future insertTilde( - EditorWidgetTester editor, { - int repeat = 1, - }) async { - for (var i = 0; i < repeat; i++) { - await editor.pressLogicKey(character: '~'); - } - } - - testWidgets('~~AppFlowy~~ to strikethrough AppFlowy', (tester) async { - const text = '~~AppFlowy~'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertTilde(editor); - final allStrikethrough = textNode.allSatisfyStrikethroughInSelection( - Selection.single( - path: [0], - startOffset: 0, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allStrikethrough, true); - expect(textNode.toPlainText(), 'AppFlowy'); - }); - - testWidgets('App~~Flowy~~ to strikethrough AppFlowy', (tester) async { - const text = 'App~~Flowy~'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertTilde(editor); - final allStrikethrough = textNode.allSatisfyStrikethroughInSelection( - Selection.single( - path: [0], - startOffset: 3, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allStrikethrough, true); - expect(textNode.toPlainText(), 'AppFlowy'); - }); - - testWidgets('~~~AppFlowy~~ to bold ~AppFlowy', (tester) async { - const text = '~~~AppFlowy~'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertTilde(editor); - final allStrikethrough = textNode.allSatisfyStrikethroughInSelection( - Selection.single( - path: [0], - startOffset: 1, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allStrikethrough, true); - expect(textNode.toPlainText(), '~AppFlowy'); - }); - - testWidgets('~~~~ nothing changes', (tester) async { - const text = '~~~'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await insertTilde(editor); - final allStrikethrough = textNode.allSatisfyStrikethroughInSelection( - Selection.single( - path: [0], - startOffset: 0, - endOffset: textNode.toPlainText().length, - ), - ); - expect(allStrikethrough, false); - expect(textNode.toPlainText(), text); - }); - }); - }); - - group('convert double asterisk to bold', () { - Future insertAsterisk( - EditorWidgetTester editor, { - int repeat = 1, - }) async { - for (var i = 0; i < repeat; i++) { - await editor.pressLogicKey(character: '*'); - } - } - - testWidgets('**AppFlowy** to bold AppFlowy', ((widgetTester) async { - const text = '**AppFlowy*'; - final editor = widgetTester.editor..insertTextNode(''); - - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [0], startOffset: 0)); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - - await insertAsterisk(editor); - - final allBold = textNode.allSatisfyBoldInSelection(Selection.single( - path: [0], startOffset: 0, endOffset: textNode.toPlainText().length)); - - expect(allBold, true); - expect(textNode.toPlainText(), 'AppFlowy'); - })); - - testWidgets('App**Flowy** to bold AppFlowy', ((widgetTester) async { - const text = 'App**Flowy*'; - final editor = widgetTester.editor..insertTextNode(''); - - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [0], startOffset: 0)); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - - await insertAsterisk(editor); - - final allBold = textNode.allSatisfyBoldInSelection(Selection.single( - path: [0], startOffset: 3, endOffset: textNode.toPlainText().length)); - - expect(allBold, true); - expect(textNode.toPlainText(), 'AppFlowy'); - })); - - testWidgets('***AppFlowy** to bold *AppFlowy', ((widgetTester) async { - const text = '***AppFlowy*'; - final editor = widgetTester.editor..insertTextNode(''); - - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [0], startOffset: 0)); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - - await insertAsterisk(editor); - - final allBold = textNode.allSatisfyBoldInSelection(Selection.single( - path: [0], startOffset: 1, endOffset: textNode.toPlainText().length)); - - expect(allBold, true); - expect(textNode.toPlainText(), '*AppFlowy'); - })); - - testWidgets('**** nothing changes', ((widgetTester) async { - const text = '***'; - final editor = widgetTester.editor..insertTextNode(''); - - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [0], startOffset: 0)); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - - await insertAsterisk(editor); - - final allBold = textNode.allSatisfyBoldInSelection(Selection.single( - path: [0], startOffset: 0, endOffset: textNode.toPlainText().length)); - - expect(allBold, false); - expect(textNode.toPlainText(), text); - })); - }); - - group('convert double underscore to bold', () { - Future insertUnderscore( - EditorWidgetTester editor, { - int repeat = 1, - }) async { - for (var i = 0; i < repeat; i++) { - await editor.pressLogicKey(character: '_'); - } - } - - testWidgets('__AppFlowy__ to bold AppFlowy', ((widgetTester) async { - const text = '__AppFlowy_'; - final editor = widgetTester.editor..insertTextNode(''); - - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [0], startOffset: 0)); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - - await insertUnderscore(editor); - - final allBold = textNode.allSatisfyBoldInSelection( - Selection.single( - path: [0], - startOffset: 0, - endOffset: textNode.toPlainText().length, - ), - ); - - expect(allBold, true); - expect(textNode.toPlainText(), 'AppFlowy'); - })); - - testWidgets('App__Flowy__ to bold AppFlowy', ((widgetTester) async { - const text = 'App__Flowy_'; - final editor = widgetTester.editor..insertTextNode(''); - - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [0], startOffset: 0)); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - - await insertUnderscore(editor); - - final allBold = textNode.allSatisfyBoldInSelection(Selection.single( - path: [0], startOffset: 3, endOffset: textNode.toPlainText().length)); - - expect(allBold, true); - expect(textNode.toPlainText(), 'AppFlowy'); - })); - - testWidgets('__*AppFlowy__ to bold *AppFlowy', ((widgetTester) async { - const text = '__*AppFlowy_'; - final editor = widgetTester.editor..insertTextNode(''); - - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [0], startOffset: 0)); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - - await insertUnderscore(editor); - - final allBold = textNode.allSatisfyBoldInSelection(Selection.single( - path: [0], startOffset: 1, endOffset: textNode.toPlainText().length)); - - expect(allBold, true); - expect(textNode.toPlainText(), '*AppFlowy'); - })); - - testWidgets('____ nothing changes', ((widgetTester) async { - const text = '___'; - final editor = widgetTester.editor..insertTextNode(''); - - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [0], startOffset: 0)); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - - await insertUnderscore(editor); - - final allBold = textNode.allSatisfyBoldInSelection(Selection.single( - path: [0], startOffset: 0, endOffset: textNode.toPlainText().length)); - - expect(allBold, false); - expect(textNode.toPlainText(), text); - })); - }); - - group('Convert single asterisk to italic', () { - testWidgets('Test Single Asterisk for Italics', (tester) async { - const text = '*Hello World'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - - await editor.pressLogicKey(character: '*'); - - final textNode = editor.nodeAtPath([0]) as TextNode; - final allItalic = textNode.allSatisfyItalicInSelection( - Selection.single( - path: [0], - startOffset: 0, - endOffset: text.length - 1, // delete the first * - ), - ); - expect(allItalic, true); - }); - - testWidgets( - 'nothing happens if there\'re more than one * precede the current position', - (tester) async { - const text = '**Hello World'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - - await editor.pressLogicKey(character: '*'); - await tester.pumpAndSettle(); - - final textNode = editor.nodeAtPath([0]) as TextNode; - final allItalic = textNode.allSatisfyItalicInSelection( - Selection.single( - path: [0], - startOffset: 0, - endOffset: text.length, // insert a new * - ), - ); - expect(allItalic, false); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart deleted file mode 100644 index 980d1ace20..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('page_up_down_handler_test.dart', () { - testWidgets('Presses PageUp and pageDown key in large document', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor; - for (var i = 0; i < 1000; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - - final scrollService = editor.editorState.service.scrollService; - - expect(scrollService != null, true); - - if (scrollService == null) { - return; - } - - final page = scrollService.page; - final onePageHeight = scrollService.onePageHeight; - expect(page != null, true); - expect(onePageHeight != null, true); - - // Pressing the pageDown key continuously. - var currentOffsetY = 0.0; - for (int i = 1; i <= page!; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.pageDown, - ); - if (i == page) { - currentOffsetY = scrollService.maxScrollExtent; - } else { - currentOffsetY += onePageHeight!; - } - final dy = scrollService.dy; - expect(dy, currentOffsetY); - } - - for (int i = 1; i <= 5; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.pageDown, - ); - final dy = scrollService.dy; - expect(dy == scrollService.maxScrollExtent, true); - } - - // Pressing the pageUp key continuously. - for (int i = page; i >= 1; i--) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.pageUp, - ); - if (i == 1) { - currentOffsetY = scrollService.minScrollExtent; - } else { - currentOffsetY -= onePageHeight!; - } - final dy = editor.editorState.service.scrollService?.dy; - expect(dy, currentOffsetY); - } - - for (int i = 1; i <= 5; i++) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.pageUp, - ); - final dy = scrollService.dy; - expect(dy == scrollService.minScrollExtent, true); - } - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart deleted file mode 100644 index d2a0bcbe2d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'dart:io'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('redo_undo_handler_test.dart', () { - // TODO: need to test more cases. - testWidgets('Redo, Undo for backspace key, and selection is downward', - (tester) async { - await _testBackspaceUndoRedo(tester, true); - }); - - testWidgets('Redo, Undo for backspace key, and selection is forward', - (tester) async { - await _testBackspaceUndoRedo(tester, false); - }); - }); -} - -Future _testBackspaceUndoRedo( - WidgetTester tester, bool isDownwardSelection) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - final start = Position(path: [0], offset: text.length); - final end = Position(path: [1], offset: text.length); - final selection = Selection( - start: isDownwardSelection ? start : end, - end: isDownwardSelection ? end : start, - ); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); - expect(editor.documentLength, 2); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyZ, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyZ, - isMetaPressed: true, - ); - } - - expect(editor.documentLength, 3); - expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text); - expect(editor.documentSelection, selection); - - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyZ, - isControlPressed: true, - isShiftPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyZ, - isMetaPressed: true, - isShiftPressed: true, - ); - } - - expect(editor.documentLength, 2); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart deleted file mode 100644 index 25a38fd435..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'dart:io'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('select_all_handler_test.dart', () { - testWidgets('Presses Command + A in small document', (tester) async { - await _testSelectAllHandler(tester, 10); - }); - - testWidgets('Presses Command + A in small document', (tester) async { - await _testSelectAllHandler(tester, 1000); - }); - }); -} - -Future _testSelectAllHandler(WidgetTester tester, int lines) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor; - for (var i = 0; i < lines; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyA, isControlPressed: true); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.keyA, isMetaPressed: true); - } - - expect( - editor.documentSelection, - Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [lines - 1], offset: text.length), - ), - ); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart deleted file mode 100644 index d998022beb..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('slash_handler.dart', () { - testWidgets('Presses / to trigger selection menu in 0 index', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - const lines = 3; - final editor = tester.editor; - for (var i = 0; i < lines; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [1], startOffset: 0)); - await editor.pressLogicKey(key: LogicalKeyboardKey.slash); - - await tester.pumpAndSettle(const Duration(milliseconds: 1000)); - - expect( - find.byType(SelectionMenuWidget, skipOffstage: false), - findsOneWidget, - ); - - for (final item in defaultSelectionMenuItems) { - expect(find.text(item.name), findsOneWidget); - } - - await editor.updateSelection(Selection.single(path: [1], startOffset: 0)); - - await tester.pumpAndSettle(const Duration(milliseconds: 200)); - - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNothing, - ); - }); - - testWidgets('Presses / to trigger selection menu in not 0 index', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - const lines = 3; - final editor = tester.editor; - for (var i = 0; i < lines; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - await editor.updateSelection(Selection.single(path: [1], startOffset: 5)); - await editor.pressLogicKey(key: LogicalKeyboardKey.slash); - - await tester.pumpAndSettle(const Duration(milliseconds: 1000)); - - expect( - find.byType(SelectionMenuWidget, skipOffstage: false), - findsOneWidget, - ); - - for (final item in defaultSelectionMenuItems) { - expect(find.text(item.name), findsOneWidget); - } - - await editor.updateSelection(Selection.single(path: [1], startOffset: 0)); - - await tester.pumpAndSettle(const Duration(milliseconds: 200)); - - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNothing, - ); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/space_on_web_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/space_on_web_handler_test.dart deleted file mode 100644 index 4e326efa74..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/space_on_web_handler_test.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('space_on_web_handler.dart', () { - testWidgets('Presses space key on web', (tester) async { - if (!kIsWeb) return; - const count = 10; - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor; - for (var i = 0; i < count; i++) { - editor.insertTextNode(text); - } - await editor.startTesting(); - - for (var i = 0; i < count; i++) { - await editor.updateSelection( - Selection.single(path: [i], startOffset: 1), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect( - (editor.nodeAtPath([i]) as TextNode).toPlainText(), - 'W elcome to Appflowy 😁', - ); - } - for (var i = 0; i < count; i++) { - await editor.updateSelection( - Selection.single(path: [i], startOffset: text.length + 1), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect( - (editor.nodeAtPath([i]) as TextNode).toPlainText(), - 'W elcome to Appflowy 😁 ', - ); - } - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/tab_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/tab_handler_test.dart deleted file mode 100644 index 5ef746a2d0..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/tab_handler_test.dart +++ /dev/null @@ -1,272 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('tab_handler.dart', () { - testWidgets('press tab in plain text', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - var selection = Selection.single(path: [0], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0], startOffset: 4), - ); - - selection = Selection.single(path: [1], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 4), - ); - }); - - testWidgets('press tab in bulleted list', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode( - text, - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList - }, - ) - ..insertTextNode( - text, - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList - }, - ) - ..insertTextNode( - text, - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList - }, - ); - await editor.startTesting(); - var document = editor.document; - - var selection = Selection.single(path: [0], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - // nothing happens - expect( - editor.documentSelection, - Selection.single(path: [0], startOffset: 0), - ); - expect(editor.document.toJson(), document.toJson()); - - // Before - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // After - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - - selection = Selection.single(path: [1], startOffset: 0); - await editor.updateSelection(selection); - - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0, 0], startOffset: 0), - ); - expect(editor.nodeAtPath([0])!.subtype, BuiltInAttributeKey.bulletedList); - expect(editor.nodeAtPath([1])!.subtype, BuiltInAttributeKey.bulletedList); - expect(editor.nodeAtPath([2]), null); - expect( - editor.nodeAtPath([0, 0])!.subtype, BuiltInAttributeKey.bulletedList); - - selection = Selection.single(path: [1], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0, 1], startOffset: 0), - ); - expect(editor.nodeAtPath([0])!.subtype, BuiltInAttributeKey.bulletedList); - expect(editor.nodeAtPath([1]), null); - expect(editor.nodeAtPath([2]), null); - expect( - editor.nodeAtPath([0, 0])!.subtype, BuiltInAttributeKey.bulletedList); - expect( - editor.nodeAtPath([0, 1])!.subtype, BuiltInAttributeKey.bulletedList); - - // Before - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // After - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - // * Welcome to Appflowy 😁 - document = editor.document; - selection = Selection.single(path: [0, 0], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0, 0], startOffset: 0), - ); - expect(editor.document.toJson(), document.toJson()); - - selection = Selection.single(path: [0, 1], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0, 0, 0], startOffset: 0), - ); - expect( - editor.nodeAtPath([0])!.subtype, - BuiltInAttributeKey.bulletedList, - ); - expect( - editor.nodeAtPath([0, 0])!.subtype, - BuiltInAttributeKey.bulletedList, - ); - expect(editor.nodeAtPath([0, 1]), null); - expect( - editor.nodeAtPath([0, 0, 0])!.subtype, - BuiltInAttributeKey.bulletedList, - ); - }); - }); - - testWidgets('press tab in checkbox/todo list', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode( - text, - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - ) - ..insertTextNode( - text, - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - ) - ..insertTextNode( - text, - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.checkbox: false, - }, - ); - await editor.startTesting(); - var document = editor.document; - - var selection = Selection.single(path: [0], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - // nothing happens - expect( - editor.documentSelection, - Selection.single(path: [0], startOffset: 0), - ); - expect(editor.document.toJson(), document.toJson()); - - // Before - // [] Welcome to Appflowy 😁 - // [] Welcome to Appflowy 😁 - // [] Welcome to Appflowy 😁 - // After - // [] Welcome to Appflowy 😁 - // [] Welcome to Appflowy 😁 - // [] Welcome to Appflowy 😁 - - selection = Selection.single(path: [1], startOffset: 0); - await editor.updateSelection(selection); - - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0, 0], startOffset: 0), - ); - expect(editor.nodeAtPath([0])!.subtype, BuiltInAttributeKey.checkbox); - expect(editor.nodeAtPath([1])!.subtype, BuiltInAttributeKey.checkbox); - expect(editor.nodeAtPath([2]), null); - expect(editor.nodeAtPath([0, 0])!.subtype, BuiltInAttributeKey.checkbox); - - selection = Selection.single(path: [1], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0, 1], startOffset: 0), - ); - expect(editor.nodeAtPath([0])!.subtype, BuiltInAttributeKey.checkbox); - expect(editor.nodeAtPath([1]), null); - expect(editor.nodeAtPath([2]), null); - expect(editor.nodeAtPath([0, 0])!.subtype, BuiltInAttributeKey.checkbox); - expect(editor.nodeAtPath([0, 1])!.subtype, BuiltInAttributeKey.checkbox); - - // Before - // [] Welcome to Appflowy 😁 - // [] Welcome to Appflowy 😁 - // [] Welcome to Appflowy 😁 - // After - // [] Welcome to Appflowy 😁 - // [] Welcome to Appflowy 😁 - // [] Welcome to Appflowy 😁 - document = editor.document; - selection = Selection.single(path: [0, 0], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0, 0], startOffset: 0), - ); - expect(editor.document.toJson(), document.toJson()); - - selection = Selection.single(path: [0, 1], startOffset: 0); - await editor.updateSelection(selection); - await editor.pressLogicKey(key: LogicalKeyboardKey.tab); - - expect( - editor.documentSelection, - Selection.single(path: [0, 0, 0], startOffset: 0), - ); - expect( - editor.nodeAtPath([0])!.subtype, - BuiltInAttributeKey.checkbox, - ); - expect( - editor.nodeAtPath([0, 0])!.subtype, - BuiltInAttributeKey.checkbox, - ); - expect(editor.nodeAtPath([0, 1]), null); - expect( - editor.nodeAtPath([0, 0, 0])!.subtype, - BuiltInAttributeKey.checkbox, - ); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart deleted file mode 100644 index 4418c4ed61..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart +++ /dev/null @@ -1,295 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/service/internal_key_event_handlers/whitespace_handler.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('white_space_handler.dart', () { - // Before - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - // After - // [h1]Welcome to Appflowy 😁 - // [h2]Welcome to Appflowy 😁 - // [h3]Welcome to Appflowy 😁 - // [h4]Welcome to Appflowy 😁 - // [h5]Welcome to Appflowy 😁 - // [h6]Welcome to Appflowy 😁 - // - testWidgets('Presses whitespace key after #*', (tester) async { - const maxSignCount = 6; - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor; - for (var i = 1; i <= maxSignCount; i++) { - editor.insertTextNode('${'#' * i}$text'); - } - await editor.startTesting(); - - for (var i = 1; i <= maxSignCount; i++) { - await editor.updateSelection( - Selection.single(path: [i - 1], startOffset: i), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - - final textNode = (editor.nodeAtPath([i - 1]) as TextNode); - - expect(textNode.subtype, BuiltInAttributeKey.heading); - // BuiltInAttributeKey.h1 ~ BuiltInAttributeKey.h6 - expect(textNode.attributes.heading, 'h$i'); - } - }); - - // Before - // - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // Welcome to Appflowy 😁 - // - // After - // [h1]##Welcome to Appflowy 😁 - // [h2]##Welcome to Appflowy 😁 - // [h3]##Welcome to Appflowy 😁 - // [h4]##Welcome to Appflowy 😁 - // [h5]##Welcome to Appflowy 😁 - // [h6]##Welcome to Appflowy 😁 - // - testWidgets('Presses whitespace key inside #*', (tester) async { - const maxSignCount = 6; - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor; - for (var i = 1; i <= maxSignCount; i++) { - editor.insertTextNode('${'###' * i}$text'); - } - await editor.startTesting(); - - for (var i = 1; i <= maxSignCount; i++) { - await editor.updateSelection( - Selection.single(path: [i - 1], startOffset: i), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - - final textNode = (editor.nodeAtPath([i - 1]) as TextNode); - - expect(textNode.subtype, BuiltInAttributeKey.heading); - // BuiltInAttributeKey.h1 ~ BuiltInAttributeKey.h6 - expect(textNode.attributes.heading, 'h$i'); - expect(textNode.toPlainText().startsWith('##'), true); - } - }); - - // Before - // - // Welcome to Appflowy 😁 - // - // After - // [h1 ~ h6]##Welcome to Appflowy 😁 - // - testWidgets('Presses whitespace key in heading styled text', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor..insertTextNode(text); - - await editor.startTesting(); - - const maxSignCount = 6; - for (var i = 1; i <= maxSignCount; i++) { - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - - final textNode = (editor.nodeAtPath([0]) as TextNode); - - await editor.insertText(textNode, '#' * i, 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - - expect(textNode.subtype, BuiltInAttributeKey.heading); - // BuiltInAttributeKey.h2 ~ BuiltInAttributeKey.h6 - expect(textNode.attributes.heading, 'h$i'); - } - }); - - testWidgets('Presses whitespace key after (un)checkbox symbols', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - - final textNode = editor.nodeAtPath([0]) as TextNode; - for (final symbol in unCheckboxListSymbols) { - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - await editor.insertText(textNode, symbol, 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.checkbox); - expect(textNode.attributes.check, false); - } - }); - - testWidgets('Presses whitespace key after checkbox symbols', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - - final textNode = editor.nodeAtPath([0]) as TextNode; - for (final symbol in checkboxListSymbols) { - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - await editor.insertText(textNode, symbol, 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.checkbox); - expect(textNode.attributes.check, true); - } - }); - - testWidgets('Presses whitespace key after bulleted list', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - - final textNode = editor.nodeAtPath([0]) as TextNode; - for (final symbol in bulletedListSymbols) { - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - await editor.insertText(textNode, symbol, 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.bulletedList); - } - }); - - testWidgets('Presses whitespace key in edge cases', (tester) async { - const text = ''; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - - final textNode = editor.nodeAtPath([0]) as TextNode; - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - - await editor.insertText(textNode, '>', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.quote); - - await editor.insertText(textNode, '*', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.bulletedList); - - await editor.insertText(textNode, '[]', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.checkbox); - expect(textNode.attributes.check, false); - - await editor.insertText(textNode, '1.', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.numberList); - - await editor.insertText(textNode, '#', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.heading); - - await editor.insertText(textNode, '[x]', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.checkbox); - expect(textNode.attributes.check, true); - - const insertedText = '[]AppFlowy'; - await editor.insertText(textNode, insertedText, 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.checkbox); - expect(textNode.attributes.check, true); - expect(textNode.toPlainText(), insertedText); - }); - - testWidgets('Presses # at the end of the text', (tester) async { - const text = 'Welcome to Appflowy 😁 #'; - final editor = tester.editor..insertTextNode(text); - await editor.startTesting(); - - final textNode = editor.nodeAtPath([0]) as TextNode; - await editor.updateSelection( - Selection.single(path: [0], startOffset: text.length), - ); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, null); - expect(textNode.toPlainText(), text); - }); - - group('convert geater to blockquote', () { - testWidgets('> AppFlowy to blockquote AppFlowy', (tester) async { - const text = 'AppFlowy'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - - final textNode = editor.nodeAtPath([0]) as TextNode; - await editor.insertText(textNode, '>', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - expect(textNode.subtype, BuiltInAttributeKey.quote); - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - expect(textNode.toPlainText(), 'AppFlowy'); - }); - - testWidgets('AppFlowy > nothing changes', (tester) async { - const text = 'AppFlowy >'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - final isQuote = textNode.subtype == BuiltInAttributeKey.quote; - expect(isQuote, false); - expect(textNode.toPlainText(), text); - }); - - testWidgets('> in front of text to blockquote', (tester) async { - const text = 'AppFlowy'; - final editor = tester.editor..insertTextNode(''); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - final textNode = editor.nodeAtPath([0]) as TextNode; - for (var i = 0; i < text.length; i++) { - await editor.insertText(textNode, text[i], i); - } - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0), - ); - await editor.insertText(textNode, '>', 0); - await editor.pressLogicKey(key: LogicalKeyboardKey.space); - - final isQuote = textNode.subtype == BuiltInAttributeKey.quote; - expect(isQuote, true); - expect(textNode.toPlainText(), text); - }); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/scroll_service_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/scroll_service_test.dart deleted file mode 100644 index cf724a731c..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/scroll_service_test.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('Testing Scroll With Gestures', () { - testWidgets('Test Gestsure Scroll', (tester) async { - final editor = tester.editor; - for (var i = 0; i < 100; i++) { - editor.insertTextNode('$i'); - } - editor.insertTextNode('mark'); - for (var i = 100; i < 200; i++) { - editor.insertTextNode('$i'); - } - await editor.startTesting(); - - final listFinder = find.byType(Scrollable); - final itemFinder = find.text('mark', findRichText: true); - - await tester.scrollUntilVisible(itemFinder, 500.0, - scrollable: listFinder); - - expect(itemFinder, findsOneWidget); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/selection_service_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/selection_service_test.dart deleted file mode 100644 index c4f8825d38..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/selection_service_test.dart +++ /dev/null @@ -1,137 +0,0 @@ -import 'dart:io'; - -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/service/context_menu/context_menu.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('selection_service.dart', () { - testWidgets('Single tap test ', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - final secondTextNode = editor.nodeAtPath([1]); - final finder = find.byKey(secondTextNode!.key); - - final rect = tester.getRect(finder); - // tap at the beginning - await tester.tapAt(rect.centerLeft); - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 0), - ); - - // tap at the ending - await tester.tapAt(rect.centerRight); - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: text.length), - ); - }); - - testWidgets('Test double tap', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - final secondTextNode = editor.nodeAtPath([1]); - final finder = find.byKey(secondTextNode!.key); - - final rect = tester.getRect(finder); - // double tap - await tester.tapAt(rect.centerLeft + const Offset(10.0, 0.0)); - await tester.tapAt(rect.centerLeft + const Offset(10.0, 0.0)); - await tester.pump(); - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 0, endOffset: 7), - ); - }); - - testWidgets('Test triple tap', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - final secondTextNode = editor.nodeAtPath([1]); - final finder = find.byKey(secondTextNode!.key); - - final rect = tester.getRect(finder); - // triple tap - await tester.tapAt(rect.centerLeft + const Offset(10.0, 0.0)); - await tester.tapAt(rect.centerLeft + const Offset(10.0, 0.0)); - await tester.tapAt(rect.centerLeft + const Offset(10.0, 0.0)); - await tester.pump(); - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 0, endOffset: text.length), - ); - }); - - testWidgets('Test secondary tap', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - final secondTextNode = editor.nodeAtPath([1]) as TextNode; - final finder = find.byKey(secondTextNode.key); - - final rect = tester.getRect(finder); - // secondary tap - await tester.tapAt( - rect.centerLeft + const Offset(10.0, 0.0), - buttons: kSecondaryButton, - ); - await tester.pump(); - - const welcome = 'Welcome'; - expect( - editor.documentSelection, - Selection.single( - path: [1], - startOffset: 0, - endOffset: welcome.length, - ), // Welcome - ); - - final contextMenu = find.byType(ContextMenu); - expect(contextMenu, findsOneWidget); - - // test built in context menu items - - // Skip the Windows platform because the rich_clipboard package doesn't support it perfectly. - if (Platform.isWindows) { - return; - } - - // cut - await tester.tap(find.text('Cut')); - await tester.pump(); - expect( - secondTextNode.toPlainText(), - text.replaceAll(welcome, ''), - ); - - // TODO: the copy and paste test is not working during test env. - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/keybinding_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/keybinding_test.dart deleted file mode 100644 index 48b335c6c7..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/keybinding_test.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/appflowy_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('keybinding_test.dart', () { - test('keybinding parse(cmd+shift+alt+ctrl+a)', () { - const command = 'cmd+shift+alt+ctrl+a'; - final keybinding = Keybinding.parse(command); - expect(keybinding.isAltPressed, true); - expect(keybinding.isShiftPressed, true); - expect(keybinding.isMetaPressed, true); - expect(keybinding.isControlPressed, true); - expect(keybinding.keyLabel, 'a'); - }); - - test('keybinding parse(cmd+shift+alt+a)', () { - const command = 'cmd+shift+alt+a'; - final keybinding = Keybinding.parse(command); - expect(keybinding.isAltPressed, true); - expect(keybinding.isShiftPressed, true); - expect(keybinding.isMetaPressed, true); - expect(keybinding.isControlPressed, false); - expect(keybinding.keyLabel, 'a'); - }); - - test('keybinding parse(cmd+shift+ctrl+a)', () { - const command = 'cmd+shift+ctrl+a'; - final keybinding = Keybinding.parse(command); - expect(keybinding.isAltPressed, false); - expect(keybinding.isShiftPressed, true); - expect(keybinding.isMetaPressed, true); - expect(keybinding.isControlPressed, true); - expect(keybinding.keyLabel, 'a'); - }); - - test('keybinding parse(cmd+alt+ctrl+a)', () { - const command = 'cmd+alt+ctrl+a'; - final keybinding = Keybinding.parse(command); - expect(keybinding.isAltPressed, true); - expect(keybinding.isShiftPressed, false); - expect(keybinding.isMetaPressed, true); - expect(keybinding.isControlPressed, true); - expect(keybinding.keyLabel, 'a'); - }); - - test('keybinding parse(shift+alt+ctrl+a)', () { - const command = 'shift+alt+ctrl+a'; - final keybinding = Keybinding.parse(command); - expect(keybinding.isAltPressed, true); - expect(keybinding.isShiftPressed, true); - expect(keybinding.isMetaPressed, false); - expect(keybinding.isControlPressed, true); - expect(keybinding.keyLabel, 'a'); - }); - - test('keybinding copyWith', () { - const command = 'shift+alt+ctrl+a'; - final keybinding = - Keybinding.parse(command).copyWith(isMetaPressed: true); - expect(keybinding.isAltPressed, true); - expect(keybinding.isShiftPressed, true); - expect(keybinding.isMetaPressed, true); - expect(keybinding.isControlPressed, true); - expect(keybinding.keyLabel, 'a'); - }); - - test('keybinding equal', () { - const command = 'cmd+shift+alt+ctrl+a'; - expect(Keybinding.parse(command), Keybinding.parse(command)); - }); - - test('keybinding toMap', () { - const command = 'cmd+shift+alt+ctrl+a'; - final keybinding = Keybinding.parse(command); - expect(keybinding, Keybinding.fromMap(keybinding.toMap())); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart deleted file mode 100644 index a8a827140d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart +++ /dev/null @@ -1,223 +0,0 @@ -import 'dart:io'; - -import 'package:appflowy_editor/src/service/shortcut_event/built_in_shortcut_events.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/appflowy_editor.dart'; -import '../../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('shortcut_event.dart', () { - test('redefine shortcut event command', () { - final shortcutEvent = ShortcutEvent( - key: 'Sample', - command: 'cmd+shift+alt+ctrl+a', - handler: (editorState, event) { - return KeyEventResult.handled; - }, - ); - shortcutEvent.updateCommand(command: 'cmd+shift+alt+ctrl+b'); - expect(shortcutEvent.keybindings.length, 1); - expect(shortcutEvent.keybindings.first.isMetaPressed, true); - expect(shortcutEvent.keybindings.first.isShiftPressed, true); - expect(shortcutEvent.keybindings.first.isAltPressed, true); - expect(shortcutEvent.keybindings.first.isControlPressed, true); - expect(shortcutEvent.keybindings.first.keyLabel, 'b'); - }); - - testWidgets('redefine move cursor begin command', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [1], startOffset: text.length), - ); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isMetaPressed: true, - ); - } - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 0), - ); - await editor.updateSelection( - Selection.single(path: [1], startOffset: text.length), - ); - - for (final event in builtInShortcutEvents) { - if (event.key == 'Move cursor begin') { - event.updateCommand( - windowsCommand: 'alt+arrow left', - linuxCommand: 'alt+arrow left', - macOSCommand: 'alt+arrow left', - ); - } - } - if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isAltPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowLeft, - isMetaPressed: true, - ); - } - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 0), - ); - - tester.pumpAndSettle(); - }); - - testWidgets('redefine move cursor end command', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0), - ); - if (Platform.isWindows || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isControlPressed: true, - ); - } else { - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isMetaPressed: true, - ); - } - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: text.length), - ); - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0), - ); - - for (final event in builtInShortcutEvents) { - if (event.key == 'Move cursor end') { - event.updateCommand( - windowsCommand: 'alt+arrow right', - linuxCommand: 'alt+arrow right', - macOSCommand: 'alt+arrow right', - ); - } - } - await editor.pressLogicKey( - key: LogicalKeyboardKey.arrowRight, - isAltPressed: true, - ); - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: text.length), - ); - }); - - testWidgets('Test Home Key to move to start of current text', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [1], startOffset: text.length), - ); - if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.home, - ); - } - - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 0), - ); - await editor.updateSelection( - Selection.single(path: [1], startOffset: text.length), - ); - - for (final event in builtInShortcutEvents) { - if (event.key == 'Move cursor begin') { - event.updateCommand( - windowsCommand: 'home', - linuxCommand: 'home', - macOSCommand: 'home', - ); - } - } - if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.home, - ); - } - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: 0), - ); - }); - - testWidgets('Test End Key to move to end of current text', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [1], startOffset: text.length), - ); - if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.end, - ); - } - - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: text.length), - ); - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0), - ); - - for (final event in builtInShortcutEvents) { - if (event.key == 'Move cursor end') { - event.updateCommand( - windowsCommand: 'end', - linuxCommand: 'end', - macOSCommand: 'end', - ); - } - } - if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { - await editor.pressLogicKey( - key: LogicalKeyboardKey.end, - ); - } - expect( - editor.documentSelection, - Selection.single(path: [1], startOffset: text.length), - ); - }); - }); -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/toolbar_service_test.dart b/frontend/appflowy_flutter/packages/appflowy_editor/test/service/toolbar_service_test.dart deleted file mode 100644 index ce6430903d..0000000000 --- a/frontend/appflowy_flutter/packages/appflowy_editor/test/service/toolbar_service_test.dart +++ /dev/null @@ -1,241 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; -import 'package:flutter_test/flutter_test.dart'; -import '../infra/test_editor.dart'; - -void main() async { - setUpAll(() { - TestWidgetsFlutterBinding.ensureInitialized(); - }); - - group('toolbar_service.dart', () { - testWidgets('Test toolbar service in multi text selection', (tester) async { - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode(text) - ..insertTextNode(text); - await editor.startTesting(); - - final selection = Selection( - start: Position(path: [0], offset: 0), - end: Position(path: [1], offset: text.length), - ); - await editor.updateSelection(selection); - - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - - // no link item - final item = defaultToolbarItems - .where((item) => item.id == 'appflowy.toolbar.link') - .first; - final finder = find.byType(ToolbarItemWidget); - - expect( - tester - .widgetList(finder) - .toList(growable: false) - .where((element) => element.item.id == item.id) - .isEmpty, - true, - ); - }); - - testWidgets( - 'Test toolbar service in single text selection with BuiltInAttributeKey.partialStyleKeys', - (tester) async { - final attributes = BuiltInAttributeKey.partialStyleKeys - .fold({}, (previousValue, element) { - if (element == BuiltInAttributeKey.backgroundColor) { - previousValue[element] = '0x6000BCF0'; - } else if (element == BuiltInAttributeKey.href) { - previousValue[element] = 'appflowy.io'; - } else { - previousValue[element] = true; - } - return previousValue; - }); - - const text = 'Welcome to Appflowy 😁'; - final editor = tester.editor - ..insertTextNode(text) - ..insertTextNode( - null, - delta: Delta(operations: [ - TextInsert(text), - TextInsert(text, attributes: attributes), - TextInsert(text), - ]), - ); - await editor.startTesting(); - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0, endOffset: text.length), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - - void testHighlight(bool expectedValue) { - for (final styleKey in BuiltInAttributeKey.partialStyleKeys) { - var key = styleKey; - if (styleKey == BuiltInAttributeKey.backgroundColor) { - key = 'highlight'; - } else if (styleKey == BuiltInAttributeKey.href) { - key = 'link'; - } else { - continue; - } - final itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.$key'); - expect(itemWidget.isHighlight, expectedValue); - } - } - - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0, endOffset: text.length * 2), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - testHighlight(false); - - await editor.updateSelection( - Selection.single( - path: [1], - startOffset: text.length, - endOffset: text.length * 2, - ), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - testHighlight(true); - - await editor.updateSelection( - Selection.single( - path: [1], - startOffset: text.length + 2, - endOffset: text.length * 2 - 2, - ), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - testHighlight(true); - }); - - testWidgets( - 'Test toolbar service in single text selection with BuiltInAttributeKey.globalStyleKeys', - (tester) async { - const text = 'Welcome to Appflowy 😁'; - - final editor = tester.editor - ..insertTextNode(text, attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h1, - }) - ..insertTextNode( - text, - attributes: {BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote}, - ) - ..insertTextNode( - text, - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList - }, - ); - await editor.startTesting(); - - await editor.updateSelection( - Selection.single(path: [0], startOffset: 0, endOffset: text.length), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - var itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.h1'); - expect(itemWidget.isHighlight, true); - - await editor.updateSelection( - Selection.single(path: [1], startOffset: 0, endOffset: text.length), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.quote'); - expect(itemWidget.isHighlight, true); - - await editor.updateSelection( - Selection.single(path: [2], startOffset: 0, endOffset: text.length), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.bulleted_list'); - expect(itemWidget.isHighlight, true); - }); - - testWidgets('Test toolbar service in multi text selection', (tester) async { - const text = 'Welcome to Appflowy 😁'; - - /// [h1][bold] Welcome to Appflowy 😁 - /// [EmptyLine] - /// Welcome to Appflowy 😁 - final editor = tester.editor - ..insertTextNode( - null, - attributes: { - BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, - BuiltInAttributeKey.heading: BuiltInAttributeKey.h1, - }, - delta: Delta(operations: [ - TextInsert(text, attributes: { - BuiltInAttributeKey.bold: true, - }) - ]), - ) - ..insertTextNode(null) - ..insertTextNode(text); - await editor.startTesting(); - - await editor.updateSelection( - Selection.single(path: [2], startOffset: text.length, endOffset: 0), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - expect( - _itemWidgetForId(tester, 'appflowy.toolbar.h1').isHighlight, - false, - ); - expect( - _itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight, - false, - ); - - await editor.updateSelection( - Selection( - start: Position(path: [2], offset: text.length), - end: Position(path: [1], offset: 0), - ), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - expect( - _itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight, - false, - ); - - await editor.updateSelection( - Selection( - start: Position(path: [2], offset: text.length), - end: Position(path: [0], offset: 0), - ), - ); - await tester.pumpAndSettle(const Duration(milliseconds: 500)); - expect(find.byType(ToolbarWidget), findsOneWidget); - expect( - _itemWidgetForId(tester, 'appflowy.toolbar.bold').isHighlight, - false, - ); - }); - }); -} - -ToolbarItemWidget _itemWidgetForId(WidgetTester tester, String id) { - final finder = find.byType(ToolbarItemWidget); - final itemWidgets = tester - .widgetList(finder) - .where((element) => element.item.id == id); - expect(itemWidgets.length, 1); - return itemWidgets.first; -} diff --git a/frontend/appflowy_flutter/packages/appflowy_editor_plugins/pubspec.yaml b/frontend/appflowy_flutter/packages/appflowy_editor_plugins/pubspec.yaml index b831cbb960..cc415d9d66 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor_plugins/pubspec.yaml +++ b/frontend/appflowy_flutter/packages/appflowy_editor_plugins/pubspec.yaml @@ -12,7 +12,7 @@ environment: dependencies: flutter: sdk: flutter - appflowy_editor: ^0.1.4 + appflowy_editor: ^0.1.5 flowy_infra: path: ../flowy_infra flowy_infra_ui: diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index c785f0285f..f767f595e2 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: "direct main" description: name: appflowy_editor - sha256: "2f28ff3ef62bfb02f13e10931fa8c091aa9f804934dfbf5d3d0af0acaf06a240" + sha256: "0768e7aee78c67c6ddd2ee8cbaa182848c9d94a98d495bd415ff0059b491ac08" url: "https://pub.dev" source: hosted - version: "0.1.4" + version: "0.1.5" appflowy_editor_plugins: dependency: "direct main" description: @@ -64,7 +64,7 @@ packages: source: path version: "0.0.1" archive: - dependency: transitive + dependency: "direct main" description: name: archive sha256: "80e5141fafcb3361653ce308776cfd7d45e6e9fbb429e14eec571382c0c5fecb" @@ -131,7 +131,7 @@ packages: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" url: "https://pub.dev" source: hosted version: "3.1.1" @@ -171,7 +171,7 @@ packages: dependency: transitive description: name: built_value - sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" + sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" url: "https://pub.dev" source: hosted version: "8.4.4" @@ -527,7 +527,7 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + sha256: c224ac897bed083dabf11f238dd11a239809b446740be0c2044608c50029ffdf url: "https://pub.dev" source: hosted version: "2.0.9" @@ -553,7 +553,7 @@ packages: dependency: "direct main" description: name: fluttertoast - sha256: "7cc92eabe01e3f1babe1571c5560b135dfc762a34e41e9056881e2196b178ec1" + sha256: "2f9c4d3f4836421f7067a28f8939814597b27614e021da9d63e5d3fb6e212d25" url: "https://pub.dev" source: hosted version: "8.2.1" @@ -638,7 +638,7 @@ packages: dependency: transitive description: name: html - sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269 + sha256: "79d498e6d6761925a34ee5ea8fa6dfef38607781d2fa91e37523474282af55cb" url: "https://pub.dev" source: hosted version: "0.15.2" @@ -763,7 +763,7 @@ packages: dependency: transitive description: name: logger - sha256: "5076f09225f91dc49289a4ccb92df2eeea9ea01cf7c26d49b3a1f04c6a49eec1" + sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7 url: "https://pub.dev" source: hosted version: "1.3.0" @@ -843,7 +843,7 @@ packages: dependency: transitive description: name: node_preamble - sha256: "8ebdbaa3b96d5285d068f80772390d27c21e1fa10fb2df6627b1b9415043608d" + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" url: "https://pub.dev" source: hosted version: "2.0.2" @@ -923,7 +923,7 @@ packages: dependency: "direct main" description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4 url: "https://pub.dev" source: hosted version: "2.0.14" @@ -931,7 +931,7 @@ packages: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7" url: "https://pub.dev" source: hosted version: "2.0.24" @@ -939,7 +939,7 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + sha256: "12eee51abdf4d34c590f043f45073adbb45514a108bd9db4491547a2fd891059" url: "https://pub.dev" source: hosted version: "2.2.0" @@ -947,7 +947,7 @@ packages: dependency: transitive description: name: path_provider_linux - sha256: "2e32f1640f07caef0d3cb993680f181c79e54a3827b997d5ee221490d131fbd9" + sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" url: "https://pub.dev" source: hosted version: "2.1.10" @@ -955,7 +955,7 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" url: "https://pub.dev" source: hosted version: "2.0.6" @@ -971,7 +971,7 @@ packages: dependency: "direct main" description: name: percent_indicator - sha256: cec41f67181fbd5322aa68b355621d1a4eea827426b8eeb613f6cbe195ff7b4a + sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c url: "https://pub.dev" source: hosted version: "4.2.3" @@ -995,7 +995,7 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" url: "https://pub.dev" source: hosted version: "2.1.4" @@ -1115,7 +1115,7 @@ packages: dependency: transitive description: name: rich_clipboard_windows - sha256: "03decfbd9bf6c50c663f8c4a0db14063e2a4d9ad5dd053fdb72875aa8bc4fd1b" + sha256: fa2a28e75ce4bcc9efc6d5d0e9788b76716cdaf3b7063c141fe8af12a315f414 url: "https://pub.dev" source: hosted version: "1.0.2" @@ -1131,7 +1131,7 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" + sha256: "78528fd87d0d08ffd3e69551173c026e8eacc7b7079c82eb6a77413957b7e394" url: "https://pub.dev" source: hosted version: "2.0.20" @@ -1139,7 +1139,7 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7" + sha256: ad423a80fe7b4e48b50d6111b3ea1027af0e959e49d485712e134863d9c1c521 url: "https://pub.dev" source: hosted version: "2.0.17" @@ -1147,7 +1147,7 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0" + sha256: "1e755f8583229f185cfca61b1d80fb2344c9d660e1c69ede5450d8f478fa5310" url: "https://pub.dev" source: hosted version: "2.1.5" @@ -1155,7 +1155,7 @@ packages: dependency: transitive description: name: shared_preferences_linux - sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874 + sha256: "3a59ed10890a8409ad0faad7bb2957dab4b92b8fbe553257b05d30ed8af2c707" url: "https://pub.dev" source: hosted version: "2.1.5" @@ -1163,7 +1163,7 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc" url: "https://pub.dev" source: hosted version: "2.1.1" @@ -1171,7 +1171,7 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + sha256: "0dc2633f215a3d4aa3184c9b2c5766f4711e4e5a6b256e62aafee41f89f1bfb8" url: "https://pub.dev" source: hosted version: "2.0.6" @@ -1179,7 +1179,7 @@ packages: dependency: transitive description: name: shared_preferences_windows - sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9" + sha256: "71bcd669bb9cdb6b39f22c4a7728b6d49e934f6cba73157ffa5a54f1eed67436" url: "https://pub.dev" source: hosted version: "2.1.5" @@ -1227,7 +1227,7 @@ packages: dependency: "direct main" description: name: sized_context - sha256: "4da0ecbb6a58afc3f51ba1984dc25eef6ee0e8176f8923b686c96ee1cfba23ac" + sha256: "9921e6c09e018132c3e1c6a18e14febbc1cc5c87a200d64ff7578cb49991f6e7" url: "https://pub.dev" source: hosted version: "1.0.0+4" @@ -1264,7 +1264,7 @@ packages: dependency: transitive description: name: source_maps - sha256: "490098075234dcedb83c5d949b4c93dad5e6b7702748de000be2b57b8e6b2427" + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" url: "https://pub.dev" source: hosted version: "0.10.12" @@ -1424,7 +1424,7 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: e8f2efc804810c0f2f5b485f49e7942179f56eabcfe81dce3387fec4bb55876b + sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" url: "https://pub.dev" source: hosted version: "6.1.10" @@ -1432,7 +1432,7 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "3e2f6dfd2c7d9cd123296cab8ef66cfc2c1a13f5845f42c7a0f365690a8a7dd1" + sha256: dd729390aa936bf1bdf5cd1bc7468ff340263f80a2c4f569416507667de8e3c8 url: "https://pub.dev" source: hosted version: "6.0.26" @@ -1440,7 +1440,7 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: "0a5af0aefdd8cf820dd739886efb1637f1f24489900204f50984634c07a54815" + sha256: "3dedc66ca3c0bef9e6a93c0999aee102556a450afcc1b7bcfeace7a424927d92" url: "https://pub.dev" source: hosted version: "6.1.3" @@ -1448,7 +1448,7 @@ packages: dependency: transitive description: name: url_launcher_linux - sha256: "318c42cba924e18180c029be69caf0a1a710191b9ec49bb42b5998fdcccee3cc" + sha256: "206fb8334a700ef7754d6a9ed119e7349bc830448098f21a69bf1b4ed038cabc" url: "https://pub.dev" source: hosted version: "3.0.4" @@ -1456,7 +1456,7 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "41988b55570df53b3dd2a7fc90c76756a963de6a8c5f8e113330cb35992e2094" + sha256: "0ef2b4f97942a16523e51256b799e9aa1843da6c60c55eefbfa9dbc2dcb8331a" url: "https://pub.dev" source: hosted version: "3.0.4" @@ -1464,7 +1464,7 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6" + sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" url: "https://pub.dev" source: hosted version: "2.1.2" @@ -1472,7 +1472,7 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "44d79408ce9f07052095ef1f9a693c258d6373dc3944249374e30eff7219ccb0" + sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa" url: "https://pub.dev" source: hosted version: "2.0.16" @@ -1480,7 +1480,7 @@ packages: dependency: transitive description: name: url_launcher_windows - sha256: b6217370f8eb1fd85c8890c539f5a639a01ab209a36db82c921ebeacefc7a615 + sha256: a83ba3607a507758669cfafb03f9de09bf6e6280c14d9b9cb18f013e406dcacd url: "https://pub.dev" source: hosted version: "3.0.5" @@ -1576,7 +1576,7 @@ packages: dependency: "direct main" description: name: window_manager - sha256: "5bdd29dc5f1f3185fc90696373a571d77968e03e5e820fb1ecdbdade3f5d8fff" + sha256: "492806c69879f0d28e95472bbe5e8d5940ac8c6e99cc07052fe14946974555ba" url: "https://pub.dev" source: hosted version: "0.3.1" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 613b205df1..1d75a8dc4a 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -42,7 +42,7 @@ dependencies: git: url: https://github.com/AppFlowy-IO/appflowy-board.git ref: a183c57 - appflowy_editor: ^0.1.4 + appflowy_editor: ^0.1.5 appflowy_popover: path: packages/appflowy_popover