init
This commit is contained in:
		
						commit
						f61200f54e
					
				
							
								
								
									
										5
									
								
								.idea/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								.idea/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					# 默认忽略的文件
 | 
				
			||||||
 | 
					/shelf/
 | 
				
			||||||
 | 
					/workspace.xml
 | 
				
			||||||
 | 
					# 基于编辑器的 HTTP 客户端请求
 | 
				
			||||||
 | 
					/httpRequests/
 | 
				
			||||||
							
								
								
									
										12
									
								
								.idea/Markdown2Html.iml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										12
									
								
								.idea/Markdown2Html.iml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<module type="WEB_MODULE" version="4">
 | 
				
			||||||
 | 
					  <component name="NewModuleRootManager">
 | 
				
			||||||
 | 
					    <content url="file://$MODULE_DIR$">
 | 
				
			||||||
 | 
					      <excludeFolder url="file://$MODULE_DIR$/.tmp" />
 | 
				
			||||||
 | 
					      <excludeFolder url="file://$MODULE_DIR$/temp" />
 | 
				
			||||||
 | 
					      <excludeFolder url="file://$MODULE_DIR$/tmp" />
 | 
				
			||||||
 | 
					    </content>
 | 
				
			||||||
 | 
					    <orderEntry type="inheritedJdk" />
 | 
				
			||||||
 | 
					    <orderEntry type="sourceFolder" forTests="false" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</module>
 | 
				
			||||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="ProjectModuleManager">
 | 
				
			||||||
 | 
					    <modules>
 | 
				
			||||||
 | 
					      <module fileurl="file://$PROJECT_DIR$/.idea/Markdown2Html.iml" filepath="$PROJECT_DIR$/.idea/Markdown2Html.iml" />
 | 
				
			||||||
 | 
					    </modules>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="VcsDirectoryMappings">
 | 
				
			||||||
 | 
					    <mapping directory="" vcs="Git" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										674
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										674
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,674 @@
 | 
				
			|||||||
 | 
					                    GNU GENERAL PUBLIC LICENSE
 | 
				
			||||||
 | 
					                       Version 3, 29 June 2007
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 | 
				
			||||||
 | 
					 Everyone is permitted to copy and distribute verbatim copies
 | 
				
			||||||
 | 
					 of this license document, but changing it is not allowed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            Preamble
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  The GNU General Public License is a free, copyleft license for
 | 
				
			||||||
 | 
					software and other kinds of works.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  The licenses for most software and other practical works are designed
 | 
				
			||||||
 | 
					to take away your freedom to share and change the works.  By contrast,
 | 
				
			||||||
 | 
					the GNU General Public License is 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.  We, the Free Software Foundation, use the
 | 
				
			||||||
 | 
					GNU General Public License for most of our software; it applies also to
 | 
				
			||||||
 | 
					any other work released this way by its authors.  You can apply it to
 | 
				
			||||||
 | 
					your programs, too.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  To protect your rights, we need to prevent others from denying you
 | 
				
			||||||
 | 
					these rights or asking you to surrender the rights.  Therefore, you have
 | 
				
			||||||
 | 
					certain responsibilities if you distribute copies of the software, or if
 | 
				
			||||||
 | 
					you modify it: responsibilities to respect the freedom of others.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  For example, if you distribute copies of such a program, whether
 | 
				
			||||||
 | 
					gratis or for a fee, you must pass on to the recipients the same
 | 
				
			||||||
 | 
					freedoms that you received.  You must make sure that they, too, receive
 | 
				
			||||||
 | 
					or can get the source code.  And you must show them these terms so they
 | 
				
			||||||
 | 
					know their rights.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Developers that use the GNU GPL protect your rights with two steps:
 | 
				
			||||||
 | 
					(1) assert copyright on the software, and (2) offer you this License
 | 
				
			||||||
 | 
					giving you legal permission to copy, distribute and/or modify it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  For the developers' and authors' protection, the GPL clearly explains
 | 
				
			||||||
 | 
					that there is no warranty for this free software.  For both users' and
 | 
				
			||||||
 | 
					authors' sake, the GPL requires that modified versions be marked as
 | 
				
			||||||
 | 
					changed, so that their problems will not be attributed erroneously to
 | 
				
			||||||
 | 
					authors of previous versions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Some devices are designed to deny users access to install or run
 | 
				
			||||||
 | 
					modified versions of the software inside them, although the manufacturer
 | 
				
			||||||
 | 
					can do so.  This is fundamentally incompatible with the aim of
 | 
				
			||||||
 | 
					protecting users' freedom to change the software.  The systematic
 | 
				
			||||||
 | 
					pattern of such abuse occurs in the area of products for individuals to
 | 
				
			||||||
 | 
					use, which is precisely where it is most unacceptable.  Therefore, we
 | 
				
			||||||
 | 
					have designed this version of the GPL to prohibit the practice for those
 | 
				
			||||||
 | 
					products.  If such problems arise substantially in other domains, we
 | 
				
			||||||
 | 
					stand ready to extend this provision to those domains in future versions
 | 
				
			||||||
 | 
					of the GPL, as needed to protect the freedom of users.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Finally, every program is threatened constantly by software patents.
 | 
				
			||||||
 | 
					States should not allow patents to restrict development and use of
 | 
				
			||||||
 | 
					software on general-purpose computers, but in those that do, we wish to
 | 
				
			||||||
 | 
					avoid the special danger that patents applied to a free program could
 | 
				
			||||||
 | 
					make it effectively proprietary.  To prevent this, the GPL assures that
 | 
				
			||||||
 | 
					patents cannot be used to render the program non-free.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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 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. Use with the GNU Affero General Public License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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 Affero 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 special requirements of the GNU Affero General Public License,
 | 
				
			||||||
 | 
					section 13, concerning interaction through a network will apply to the
 | 
				
			||||||
 | 
					combination as such.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  14. Revised Versions of this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  The Free Software Foundation may publish revised and/or new versions of
 | 
				
			||||||
 | 
					the GNU 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 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 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 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <one line to give the program's name and a brief idea of what it does.>
 | 
				
			||||||
 | 
					    Copyright (C) <year>  <name of author>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					    it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					    the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					    (at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					    GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Also add information on how to contact you by electronic and paper mail.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  If the program does terminal interaction, make it output a short
 | 
				
			||||||
 | 
					notice like this when it starts in an interactive mode:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <program>  Copyright (C) <year>  <name of author>
 | 
				
			||||||
 | 
					    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 | 
				
			||||||
 | 
					    This is free software, and you are welcome to redistribute it
 | 
				
			||||||
 | 
					    under certain conditions; type `show c' for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The hypothetical commands `show w' and `show c' should show the appropriate
 | 
				
			||||||
 | 
					parts of the General Public License.  Of course, your program's commands
 | 
				
			||||||
 | 
					might be different; for a GUI interface, you would use an "about box".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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 GPL, see
 | 
				
			||||||
 | 
					<http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  The GNU General Public License does not permit incorporating your program
 | 
				
			||||||
 | 
					into proprietary programs.  If your program is a subroutine library, you
 | 
				
			||||||
 | 
					may consider it more useful to permit linking proprietary applications with
 | 
				
			||||||
 | 
					the library.  If this is what you want to do, use the GNU Lesser General
 | 
				
			||||||
 | 
					Public License instead of this License.  But first, please read
 | 
				
			||||||
 | 
					<http://www.gnu.org/philosophy/why-not-lgpl.html>.
 | 
				
			||||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					<div align="center">
 | 
				
			||||||
 | 
					<a href="https://md.luckday.cn">
 | 
				
			||||||
 | 
					<img width="500" src="./screenshot.jpg"/>
 | 
				
			||||||
 | 
					</a>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<h1 align="center">Markdown2Html</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 简介
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Fork 自 [markdown2html](https://github.com/TaleAi/markdown2html),略有调整。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- 支持自定义样式的 Markdown 编辑器
 | 
				
			||||||
 | 
					- 支持微信公众号、知乎和稀土掘金
 | 
				
			||||||
 | 
					- 支持公式
 | 
				
			||||||
 | 
					- 支持 html 转 markdwon
 | 
				
			||||||
 | 
					- 支持导出 pdf 和 markdown
 | 
				
			||||||
 | 
					- 在线使用:
 | 
				
			||||||
 | 
					  - https://md.luckday.cn/
 | 
				
			||||||
							
								
								
									
										93
									
								
								config/env.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										93
									
								
								config/env.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fs = require('fs');
 | 
				
			||||||
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					const paths = require('./paths');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Make sure that including paths.js after env.js will read .env variables.
 | 
				
			||||||
 | 
					delete require.cache[require.resolve('./paths')];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NODE_ENV = process.env.NODE_ENV;
 | 
				
			||||||
 | 
					if (!NODE_ENV) {
 | 
				
			||||||
 | 
					  throw new Error(
 | 
				
			||||||
 | 
					    'The NODE_ENV environment variable is required but was not specified.'
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
 | 
				
			||||||
 | 
					var dotenvFiles = [
 | 
				
			||||||
 | 
					  `${paths.dotenv}.${NODE_ENV}.local`,
 | 
				
			||||||
 | 
					  `${paths.dotenv}.${NODE_ENV}`,
 | 
				
			||||||
 | 
					  // Don't include `.env.local` for `test` environment
 | 
				
			||||||
 | 
					  // since normally you expect tests to produce the same
 | 
				
			||||||
 | 
					  // results for everyone
 | 
				
			||||||
 | 
					  NODE_ENV !== 'test' && `${paths.dotenv}.local`,
 | 
				
			||||||
 | 
					  paths.dotenv,
 | 
				
			||||||
 | 
					].filter(Boolean);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Load environment variables from .env* files. Suppress warnings using silent
 | 
				
			||||||
 | 
					// if this file is missing. dotenv will never modify any environment variables
 | 
				
			||||||
 | 
					// that have already been set.  Variable expansion is supported in .env files.
 | 
				
			||||||
 | 
					// https://github.com/motdotla/dotenv
 | 
				
			||||||
 | 
					// https://github.com/motdotla/dotenv-expand
 | 
				
			||||||
 | 
					dotenvFiles.forEach(dotenvFile => {
 | 
				
			||||||
 | 
					  if (fs.existsSync(dotenvFile)) {
 | 
				
			||||||
 | 
					    require('dotenv-expand')(
 | 
				
			||||||
 | 
					      require('dotenv').config({
 | 
				
			||||||
 | 
					        path: dotenvFile,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We support resolving modules according to `NODE_PATH`.
 | 
				
			||||||
 | 
					// This lets you use absolute paths in imports inside large monorepos:
 | 
				
			||||||
 | 
					// https://github.com/facebook/create-react-app/issues/253.
 | 
				
			||||||
 | 
					// It works similar to `NODE_PATH` in Node itself:
 | 
				
			||||||
 | 
					// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
 | 
				
			||||||
 | 
					// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
 | 
				
			||||||
 | 
					// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
 | 
				
			||||||
 | 
					// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
 | 
				
			||||||
 | 
					// We also resolve them to make sure all tools using them work consistently.
 | 
				
			||||||
 | 
					const appDirectory = fs.realpathSync(process.cwd());
 | 
				
			||||||
 | 
					process.env.NODE_PATH = (process.env.NODE_PATH || '')
 | 
				
			||||||
 | 
					  .split(path.delimiter)
 | 
				
			||||||
 | 
					  .filter(folder => folder && !path.isAbsolute(folder))
 | 
				
			||||||
 | 
					  .map(folder => path.resolve(appDirectory, folder))
 | 
				
			||||||
 | 
					  .join(path.delimiter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
 | 
				
			||||||
 | 
					// injected into the application via DefinePlugin in Webpack configuration.
 | 
				
			||||||
 | 
					const REACT_APP = /^REACT_APP_/i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getClientEnvironment(publicUrl) {
 | 
				
			||||||
 | 
					  const raw = Object.keys(process.env)
 | 
				
			||||||
 | 
					    .filter(key => REACT_APP.test(key))
 | 
				
			||||||
 | 
					    .reduce(
 | 
				
			||||||
 | 
					      (env, key) => {
 | 
				
			||||||
 | 
					        env[key] = process.env[key];
 | 
				
			||||||
 | 
					        return env;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        // Useful for determining whether we’re running in production mode.
 | 
				
			||||||
 | 
					        // Most importantly, it switches React into the correct mode.
 | 
				
			||||||
 | 
					        NODE_ENV: process.env.NODE_ENV || 'development',
 | 
				
			||||||
 | 
					        // Useful for resolving the correct path to static assets in `public`.
 | 
				
			||||||
 | 
					        // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
 | 
				
			||||||
 | 
					        // This should only be used as an escape hatch. Normally you would put
 | 
				
			||||||
 | 
					        // images into the `src` and `import` them in code to get their paths.
 | 
				
			||||||
 | 
					        PUBLIC_URL: publicUrl,
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  // Stringify all values so we can feed into Webpack DefinePlugin
 | 
				
			||||||
 | 
					  const stringified = {
 | 
				
			||||||
 | 
					    'process.env': Object.keys(raw).reduce((env, key) => {
 | 
				
			||||||
 | 
					      env[key] = JSON.stringify(raw[key]);
 | 
				
			||||||
 | 
					      return env;
 | 
				
			||||||
 | 
					    }, {}),
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return { raw, stringified };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = getClientEnvironment;
 | 
				
			||||||
							
								
								
									
										14
									
								
								config/jest/cssTransform.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										14
									
								
								config/jest/cssTransform.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is a custom Jest transformer turning style imports into empty objects.
 | 
				
			||||||
 | 
					// http://facebook.github.io/jest/docs/en/webpack.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  process() {
 | 
				
			||||||
 | 
					    return 'module.exports = {};';
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  getCacheKey() {
 | 
				
			||||||
 | 
					    // The output is always the same.
 | 
				
			||||||
 | 
					    return 'cssTransform';
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										30
									
								
								config/jest/fileTransform.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										30
									
								
								config/jest/fileTransform.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is a custom Jest transformer turning file imports into filenames.
 | 
				
			||||||
 | 
					// http://facebook.github.io/jest/docs/en/webpack.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  process(src, filename) {
 | 
				
			||||||
 | 
					    const assetFilename = JSON.stringify(path.basename(filename));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (filename.match(/\.svg$/)) {
 | 
				
			||||||
 | 
					      return `module.exports = {
 | 
				
			||||||
 | 
					        __esModule: true,
 | 
				
			||||||
 | 
					        default: ${assetFilename},
 | 
				
			||||||
 | 
					        ReactComponent: (props) => ({
 | 
				
			||||||
 | 
					          $$typeof: Symbol.for('react.element'),
 | 
				
			||||||
 | 
					          type: 'svg',
 | 
				
			||||||
 | 
					          ref: null,
 | 
				
			||||||
 | 
					          key: null,
 | 
				
			||||||
 | 
					          props: Object.assign({}, props, {
 | 
				
			||||||
 | 
					            children: ${assetFilename}
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      };`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return `module.exports = ${assetFilename};`;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										84
									
								
								config/paths.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										84
									
								
								config/paths.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const path = require("path");
 | 
				
			||||||
 | 
					const fs = require("fs");
 | 
				
			||||||
 | 
					const url = require("url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Make sure any symlinks in the project folder are resolved:
 | 
				
			||||||
 | 
					// https://github.com/facebook/create-react-app/issues/637
 | 
				
			||||||
 | 
					const appDirectory = fs.realpathSync(process.cwd());
 | 
				
			||||||
 | 
					const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const envPublicUrl = process.env.PUBLIC_URL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function ensureSlash(inputPath, needsSlash) {
 | 
				
			||||||
 | 
					  const hasSlash = inputPath.endsWith("/");
 | 
				
			||||||
 | 
					  if (hasSlash && !needsSlash) {
 | 
				
			||||||
 | 
					    return inputPath.substr(0, inputPath.length - 1);
 | 
				
			||||||
 | 
					  } else if (!hasSlash && needsSlash) {
 | 
				
			||||||
 | 
					    return `${inputPath}/`;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return inputPath;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getPublicUrl = (appPackageJson) => envPublicUrl || require(appPackageJson).homepage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We use `PUBLIC_URL` environment variable or "homepage" field to infer
 | 
				
			||||||
 | 
					// "public path" at which the app is served.
 | 
				
			||||||
 | 
					// Webpack needs to know it to put the right <script> hrefs into HTML even in
 | 
				
			||||||
 | 
					// single-page apps that may serve index.html for nested URLs like /todos/42.
 | 
				
			||||||
 | 
					// We can't use a relative path in HTML because we don't want to load something
 | 
				
			||||||
 | 
					// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
 | 
				
			||||||
 | 
					function getServedPath(appPackageJson) {
 | 
				
			||||||
 | 
					  const publicUrl = getPublicUrl(appPackageJson);
 | 
				
			||||||
 | 
					  const servedUrl = envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : "/");
 | 
				
			||||||
 | 
					  return ensureSlash(servedUrl, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const moduleFileExtensions = [
 | 
				
			||||||
 | 
					  "web.mjs",
 | 
				
			||||||
 | 
					  "mjs",
 | 
				
			||||||
 | 
					  "web.js",
 | 
				
			||||||
 | 
					  "js",
 | 
				
			||||||
 | 
					  "web.ts",
 | 
				
			||||||
 | 
					  "ts",
 | 
				
			||||||
 | 
					  "web.tsx",
 | 
				
			||||||
 | 
					  "tsx",
 | 
				
			||||||
 | 
					  "json",
 | 
				
			||||||
 | 
					  "web.jsx",
 | 
				
			||||||
 | 
					  "jsx",
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Resolve file paths in the same order as webpack
 | 
				
			||||||
 | 
					const resolveModule = (resolveFn, filePath) => {
 | 
				
			||||||
 | 
					  const extension = moduleFileExtensions.find((extension) => fs.existsSync(resolveFn(`${filePath}.${extension}`)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (extension) {
 | 
				
			||||||
 | 
					    return resolveFn(`${filePath}.${extension}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return resolveFn(`${filePath}.js`);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// config after eject: we're in ./config/
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  dotenv: resolveApp(".env"),
 | 
				
			||||||
 | 
					  appPath: resolveApp("."),
 | 
				
			||||||
 | 
					  appBuild: resolveApp("build"),
 | 
				
			||||||
 | 
					  appPublic: resolveApp("public"),
 | 
				
			||||||
 | 
					  appHtml: resolveApp("public/index.html"),
 | 
				
			||||||
 | 
					  appIndexJs: resolveModule(resolveApp, "src/index"),
 | 
				
			||||||
 | 
					  appLib: resolveModule(resolveApp, "src/Lib"),
 | 
				
			||||||
 | 
					  appPackageJson: resolveApp("package.json"),
 | 
				
			||||||
 | 
					  appSrc: resolveApp("src"),
 | 
				
			||||||
 | 
					  appTsConfig: resolveApp("tsconfig.json"),
 | 
				
			||||||
 | 
					  yarnLockFile: resolveApp("yarn.lock"),
 | 
				
			||||||
 | 
					  testsSetup: resolveModule(resolveApp, "src/setupTests"),
 | 
				
			||||||
 | 
					  proxySetup: resolveApp("src/setupProxy.js"),
 | 
				
			||||||
 | 
					  appNodeModules: resolveApp("node_modules"),
 | 
				
			||||||
 | 
					  publicUrl: getPublicUrl(resolveApp("package.json")),
 | 
				
			||||||
 | 
					  servedPath: getServedPath(resolveApp("package.json")),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports.moduleFileExtensions = moduleFileExtensions;
 | 
				
			||||||
							
								
								
									
										608
									
								
								config/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										608
									
								
								config/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,608 @@
 | 
				
			|||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fs = require("fs");
 | 
				
			||||||
 | 
					const path = require("path");
 | 
				
			||||||
 | 
					const webpack = require("webpack");
 | 
				
			||||||
 | 
					const resolve = require("resolve");
 | 
				
			||||||
 | 
					const PnpWebpackPlugin = require("pnp-webpack-plugin");
 | 
				
			||||||
 | 
					const HtmlWebpackPlugin = require("html-webpack-plugin");
 | 
				
			||||||
 | 
					const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
 | 
				
			||||||
 | 
					const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
 | 
				
			||||||
 | 
					const TerserPlugin = require("terser-webpack-plugin");
 | 
				
			||||||
 | 
					const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 | 
				
			||||||
 | 
					const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
 | 
				
			||||||
 | 
					const safePostCssParser = require("postcss-safe-parser");
 | 
				
			||||||
 | 
					const ManifestPlugin = require("webpack-manifest-plugin");
 | 
				
			||||||
 | 
					const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin");
 | 
				
			||||||
 | 
					const WorkboxWebpackPlugin = require("workbox-webpack-plugin");
 | 
				
			||||||
 | 
					const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin");
 | 
				
			||||||
 | 
					const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
 | 
				
			||||||
 | 
					const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent");
 | 
				
			||||||
 | 
					const paths = require("./paths");
 | 
				
			||||||
 | 
					const getClientEnvironment = require("./env");
 | 
				
			||||||
 | 
					const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin");
 | 
				
			||||||
 | 
					const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin-alt");
 | 
				
			||||||
 | 
					const typescriptFormatter = require("react-dev-utils/typescriptFormatter");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Source maps are resource heavy and can cause out of memory issue for large source files.
 | 
				
			||||||
 | 
					const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
 | 
				
			||||||
 | 
					// Some apps do not need the benefits of saving a web request, so not inlining the chunk
 | 
				
			||||||
 | 
					// makes for a smoother build process.
 | 
				
			||||||
 | 
					const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check if TypeScript is setup
 | 
				
			||||||
 | 
					const useTypeScript = fs.existsSync(paths.appTsConfig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// style files regexes
 | 
				
			||||||
 | 
					const cssRegex = /\.css$/;
 | 
				
			||||||
 | 
					const cssModuleRegex = /\.module\.css$/;
 | 
				
			||||||
 | 
					const sassRegex = /\.(scss|sass)$/;
 | 
				
			||||||
 | 
					const sassModuleRegex = /\.module\.(scss|sass)$/;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the production and development configuration.
 | 
				
			||||||
 | 
					// It is focused on developer experience, fast rebuilds, and a minimal bundle.
 | 
				
			||||||
 | 
					module.exports = function(webpackEnv) {
 | 
				
			||||||
 | 
					  const isEnvDevelopment = webpackEnv === "development";
 | 
				
			||||||
 | 
					  const isEnvProduction = webpackEnv === "production";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Webpack uses `publicPath` to determine where the app is being served from.
 | 
				
			||||||
 | 
					  // It requires a trailing slash, or the file assets will get an incorrect path.
 | 
				
			||||||
 | 
					  // In development, we always serve from the root. This makes config easier.
 | 
				
			||||||
 | 
					  const publicPath = isEnvProduction ? paths.servedPath : isEnvDevelopment && "/";
 | 
				
			||||||
 | 
					  // Some apps do not use client-side routing with pushState.
 | 
				
			||||||
 | 
					  // For these, "homepage" can be set to "." to enable relative asset paths.
 | 
				
			||||||
 | 
					  const shouldUseRelativeAssetPaths = publicPath === "./";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // `publicUrl` is just like `publicPath`, but we will provide it to our app
 | 
				
			||||||
 | 
					  // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
 | 
				
			||||||
 | 
					  // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
 | 
				
			||||||
 | 
					  const publicUrl = isEnvProduction ? publicPath.slice(0, -1) : isEnvDevelopment && "";
 | 
				
			||||||
 | 
					  // Get environment variables to inject into our app.
 | 
				
			||||||
 | 
					  const env = getClientEnvironment(publicUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // common function to get style loaders
 | 
				
			||||||
 | 
					  const getStyleLoaders = (cssOptions, preProcessor) => {
 | 
				
			||||||
 | 
					    const loaders = [
 | 
				
			||||||
 | 
					      isEnvDevelopment && require.resolve("style-loader"),
 | 
				
			||||||
 | 
					      isEnvProduction && {
 | 
				
			||||||
 | 
					        loader: MiniCssExtractPlugin.loader,
 | 
				
			||||||
 | 
					        options: Object.assign({}, shouldUseRelativeAssetPaths ? {publicPath: "../../"} : undefined),
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        loader: require.resolve("css-loader"),
 | 
				
			||||||
 | 
					        options: cssOptions,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        // Options for PostCSS as we reference these options twice
 | 
				
			||||||
 | 
					        // Adds vendor prefixing based on your specified browser support in
 | 
				
			||||||
 | 
					        // package.json
 | 
				
			||||||
 | 
					        loader: require.resolve("postcss-loader"),
 | 
				
			||||||
 | 
					        options: {
 | 
				
			||||||
 | 
					          // Necessary for external CSS imports to work
 | 
				
			||||||
 | 
					          // https://github.com/facebook/create-react-app/issues/2677
 | 
				
			||||||
 | 
					          ident: "postcss",
 | 
				
			||||||
 | 
					          plugins: () => [
 | 
				
			||||||
 | 
					            require("postcss-flexbugs-fixes"),
 | 
				
			||||||
 | 
					            require("postcss-preset-env")({
 | 
				
			||||||
 | 
					              autoprefixer: {
 | 
				
			||||||
 | 
					                flexbox: "no-2009",
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              stage: 3,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ].filter(Boolean);
 | 
				
			||||||
 | 
					    if (preProcessor) {
 | 
				
			||||||
 | 
					      loaders.push({
 | 
				
			||||||
 | 
					        loader: require.resolve(preProcessor),
 | 
				
			||||||
 | 
					        options: {
 | 
				
			||||||
 | 
					          sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return loaders;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    mode: isEnvProduction ? "production" : isEnvDevelopment && "development",
 | 
				
			||||||
 | 
					    // Stop compilation early in production
 | 
				
			||||||
 | 
					    bail: isEnvProduction,
 | 
				
			||||||
 | 
					    devtool: isEnvProduction ? (shouldUseSourceMap ? "source-map" : false) : isEnvDevelopment && "eval-source-map",
 | 
				
			||||||
 | 
					    // These are the "entry points" to our application.
 | 
				
			||||||
 | 
					    // This means they will be the "root" imports that are included in JS bundle.
 | 
				
			||||||
 | 
					    entry: [
 | 
				
			||||||
 | 
					      // Include an alternative client for WebpackDevServer. A client's job is to
 | 
				
			||||||
 | 
					      // connect to WebpackDevServer by a socket and get notified about changes.
 | 
				
			||||||
 | 
					      // When you save a file, the client will either apply hot updates (in case
 | 
				
			||||||
 | 
					      // of CSS changes), or refresh the page (in case of JS changes). When you
 | 
				
			||||||
 | 
					      // make a syntax error, this client will display a syntax error overlay.
 | 
				
			||||||
 | 
					      // Note: instead of the default WebpackDevServer client, we use a custom one
 | 
				
			||||||
 | 
					      // to bring better experience for Create React App users. You can replace
 | 
				
			||||||
 | 
					      // the line below with these two lines if you prefer the stock client:
 | 
				
			||||||
 | 
					      // require.resolve('webpack-dev-server/client') + '?/',
 | 
				
			||||||
 | 
					      // require.resolve('webpack/hot/dev-server'),
 | 
				
			||||||
 | 
					      isEnvDevelopment && require.resolve("react-dev-utils/webpackHotDevClient"),
 | 
				
			||||||
 | 
					      // Finally, this is your app's code:
 | 
				
			||||||
 | 
					      paths.appIndexJs,
 | 
				
			||||||
 | 
					      // We include the app code last so that if there is a runtime error during
 | 
				
			||||||
 | 
					      // initialization, it doesn't blow up the WebpackDevServer client, and
 | 
				
			||||||
 | 
					      // changing JS code would still trigger a refresh.
 | 
				
			||||||
 | 
					    ].filter(Boolean),
 | 
				
			||||||
 | 
					    output: {
 | 
				
			||||||
 | 
					      // The build folder.
 | 
				
			||||||
 | 
					      path: isEnvProduction ? paths.appBuild : undefined,
 | 
				
			||||||
 | 
					      // Add /* filename */ comments to generated require()s in the output.
 | 
				
			||||||
 | 
					      pathinfo: isEnvDevelopment,
 | 
				
			||||||
 | 
					      // There will be one main bundle, and one file per asynchronous chunk.
 | 
				
			||||||
 | 
					      // In development, it does not produce real files.
 | 
				
			||||||
 | 
					      filename: isEnvProduction ? "static/js/[name].[chunkhash:8].js" : isEnvDevelopment && "static/js/bundle.js",
 | 
				
			||||||
 | 
					      // There are also additional JS chunk files if you use code splitting.
 | 
				
			||||||
 | 
					      chunkFilename: isEnvProduction
 | 
				
			||||||
 | 
					        ? "static/js/[name].[chunkhash:8].chunk.js"
 | 
				
			||||||
 | 
					        : isEnvDevelopment && "static/js/[name].chunk.js",
 | 
				
			||||||
 | 
					      // We inferred the "public path" (such as / or /my-project) from homepage.
 | 
				
			||||||
 | 
					      // We use "/" in development.
 | 
				
			||||||
 | 
					      publicPath: publicPath,
 | 
				
			||||||
 | 
					      // Point sourcemap entries to original disk location (format as URL on Windows)
 | 
				
			||||||
 | 
					      devtoolModuleFilenameTemplate: isEnvProduction
 | 
				
			||||||
 | 
					        ? (info) => path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, "/")
 | 
				
			||||||
 | 
					        : isEnvDevelopment && ((info) => path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    optimization: {
 | 
				
			||||||
 | 
					      minimize: isEnvProduction,
 | 
				
			||||||
 | 
					      minimizer: [
 | 
				
			||||||
 | 
					        // This is only used in production mode
 | 
				
			||||||
 | 
					        new TerserPlugin({
 | 
				
			||||||
 | 
					          terserOptions: {
 | 
				
			||||||
 | 
					            parse: {
 | 
				
			||||||
 | 
					              // we want terser to parse ecma 8 code. However, we don't want it
 | 
				
			||||||
 | 
					              // to apply any minfication steps that turns valid ecma 5 code
 | 
				
			||||||
 | 
					              // into invalid ecma 5 code. This is why the 'compress' and 'output'
 | 
				
			||||||
 | 
					              // sections only apply transformations that are ecma 5 safe
 | 
				
			||||||
 | 
					              // https://github.com/facebook/create-react-app/pull/4234
 | 
				
			||||||
 | 
					              ecma: 8,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            compress: {
 | 
				
			||||||
 | 
					              ecma: 5,
 | 
				
			||||||
 | 
					              warnings: false,
 | 
				
			||||||
 | 
					              // Disabled because of an issue with Uglify breaking seemingly valid code:
 | 
				
			||||||
 | 
					              // https://github.com/facebook/create-react-app/issues/2376
 | 
				
			||||||
 | 
					              // Pending further investigation:
 | 
				
			||||||
 | 
					              // https://github.com/mishoo/UglifyJS2/issues/2011
 | 
				
			||||||
 | 
					              comparisons: false,
 | 
				
			||||||
 | 
					              // Disabled because of an issue with Terser breaking valid code:
 | 
				
			||||||
 | 
					              // https://github.com/facebook/create-react-app/issues/5250
 | 
				
			||||||
 | 
					              // Pending futher investigation:
 | 
				
			||||||
 | 
					              // https://github.com/terser-js/terser/issues/120
 | 
				
			||||||
 | 
					              inline: 2,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            mangle: {
 | 
				
			||||||
 | 
					              safari10: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            output: {
 | 
				
			||||||
 | 
					              ecma: 5,
 | 
				
			||||||
 | 
					              comments: false,
 | 
				
			||||||
 | 
					              // Turned on because emoji and regex is not minified properly using default
 | 
				
			||||||
 | 
					              // https://github.com/facebook/create-react-app/issues/2488
 | 
				
			||||||
 | 
					              ascii_only: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          // Use multi-process parallel running to improve the build speed
 | 
				
			||||||
 | 
					          // Default number of concurrent runs: os.cpus().length - 1
 | 
				
			||||||
 | 
					          parallel: true,
 | 
				
			||||||
 | 
					          // Enable file caching
 | 
				
			||||||
 | 
					          cache: true,
 | 
				
			||||||
 | 
					          sourceMap: shouldUseSourceMap,
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					        // This is only used in production mode
 | 
				
			||||||
 | 
					        new OptimizeCSSAssetsPlugin({
 | 
				
			||||||
 | 
					          cssProcessorOptions: {
 | 
				
			||||||
 | 
					            parser: safePostCssParser,
 | 
				
			||||||
 | 
					            map: shouldUseSourceMap
 | 
				
			||||||
 | 
					              ? {
 | 
				
			||||||
 | 
					                  // `inline: false` forces the sourcemap to be output into a
 | 
				
			||||||
 | 
					                  // separate file
 | 
				
			||||||
 | 
					                  inline: false,
 | 
				
			||||||
 | 
					                  // `annotation: true` appends the sourceMappingURL to the end of
 | 
				
			||||||
 | 
					                  // the css file, helping the browser find the sourcemap
 | 
				
			||||||
 | 
					                  annotation: true,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              : false,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      // Automatically split vendor and commons
 | 
				
			||||||
 | 
					      // https://twitter.com/wSokra/status/969633336732905474
 | 
				
			||||||
 | 
					      // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
 | 
				
			||||||
 | 
					      splitChunks: {
 | 
				
			||||||
 | 
					        chunks: "all",
 | 
				
			||||||
 | 
					        name: false,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      // Keep the runtime chunk separated to enable long term caching
 | 
				
			||||||
 | 
					      // https://twitter.com/wSokra/status/969679223278505985
 | 
				
			||||||
 | 
					      runtimeChunk: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    resolve: {
 | 
				
			||||||
 | 
					      // This allows you to set a fallback for where Webpack should look for modules.
 | 
				
			||||||
 | 
					      // We placed these paths second because we want `node_modules` to "win"
 | 
				
			||||||
 | 
					      // if there are any conflicts. This matches Node resolution mechanism.
 | 
				
			||||||
 | 
					      // https://github.com/facebook/create-react-app/issues/253
 | 
				
			||||||
 | 
					      modules: ["node_modules"].concat(
 | 
				
			||||||
 | 
					        // It is guaranteed to exist because we tweak it in `env.js`
 | 
				
			||||||
 | 
					        process.env.NODE_PATH.split(path.delimiter).filter(Boolean),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      // These are the reasonable defaults supported by the Node ecosystem.
 | 
				
			||||||
 | 
					      // We also include JSX as a common component filename extension to support
 | 
				
			||||||
 | 
					      // some tools, although we do not recommend using it, see:
 | 
				
			||||||
 | 
					      // https://github.com/facebook/create-react-app/issues/290
 | 
				
			||||||
 | 
					      // `web` extension prefixes have been added for better support
 | 
				
			||||||
 | 
					      // for React Native Web.
 | 
				
			||||||
 | 
					      extensions: paths.moduleFileExtensions
 | 
				
			||||||
 | 
					        .map((ext) => `.${ext}`)
 | 
				
			||||||
 | 
					        .filter((ext) => useTypeScript || !ext.includes("ts")),
 | 
				
			||||||
 | 
					      alias: {
 | 
				
			||||||
 | 
					        // Support React Native Web
 | 
				
			||||||
 | 
					        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
 | 
				
			||||||
 | 
					        "react-native": "react-native-web",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      plugins: [
 | 
				
			||||||
 | 
					        // Adds support for installing with Plug'n'Play, leading to faster installs and adding
 | 
				
			||||||
 | 
					        // guards against forgotten dependencies and such.
 | 
				
			||||||
 | 
					        PnpWebpackPlugin,
 | 
				
			||||||
 | 
					        // Prevents users from importing files from outside of src/ (or node_modules/).
 | 
				
			||||||
 | 
					        // This often causes confusion because we only process files within src/ with babel.
 | 
				
			||||||
 | 
					        // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
 | 
				
			||||||
 | 
					        // please link the files into your node_modules/ and let module-resolution kick in.
 | 
				
			||||||
 | 
					        // Make sure your source files are compiled, as they will not be processed in any way.
 | 
				
			||||||
 | 
					        new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    resolveLoader: {
 | 
				
			||||||
 | 
					      plugins: [
 | 
				
			||||||
 | 
					        // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
 | 
				
			||||||
 | 
					        // from the current package.
 | 
				
			||||||
 | 
					        PnpWebpackPlugin.moduleLoader(module),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    module: {
 | 
				
			||||||
 | 
					      strictExportPresence: true,
 | 
				
			||||||
 | 
					      rules: [
 | 
				
			||||||
 | 
					        // Disable require.ensure as it's not a standard language feature.
 | 
				
			||||||
 | 
					        {parser: {requireEnsure: false}},
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          // "oneOf" will traverse all following loaders until one will
 | 
				
			||||||
 | 
					          // match the requirements. When no loader matches it will fall
 | 
				
			||||||
 | 
					          // back to the "file" loader at the end of the loader list.
 | 
				
			||||||
 | 
					          oneOf: [
 | 
				
			||||||
 | 
					            // "url" loader works like "file" loader except that it embeds assets
 | 
				
			||||||
 | 
					            // smaller than specified limit in bytes as data URLs to avoid requests.
 | 
				
			||||||
 | 
					            // A missing `test` is equivalent to a match.
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
 | 
				
			||||||
 | 
					              loader: require.resolve("url-loader"),
 | 
				
			||||||
 | 
					              options: {
 | 
				
			||||||
 | 
					                limit: 10000,
 | 
				
			||||||
 | 
					                name: "static/media/[name].[hash:8].[ext]",
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Process application JS with Babel.
 | 
				
			||||||
 | 
					            // The preset includes JSX, Flow, TypeScript, and some ESnext features.
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: /\.(js|mjs|jsx|ts|tsx)$/,
 | 
				
			||||||
 | 
					              include: paths.appSrc,
 | 
				
			||||||
 | 
					              loader: require.resolve("babel-loader"),
 | 
				
			||||||
 | 
					              options: {
 | 
				
			||||||
 | 
					                customize: require.resolve("babel-preset-react-app/webpack-overrides"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                plugins: [
 | 
				
			||||||
 | 
					                  [
 | 
				
			||||||
 | 
					                    require.resolve("babel-plugin-named-asset-import"),
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                      loaderMap: {
 | 
				
			||||||
 | 
					                        svg: {
 | 
				
			||||||
 | 
					                          ReactComponent: "@svgr/webpack?-svgo![path]",
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                      },
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  ],
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                // This is a feature of `babel-loader` for webpack (not Babel itself).
 | 
				
			||||||
 | 
					                // It enables caching results in ./node_modules/.cache/babel-loader/
 | 
				
			||||||
 | 
					                // directory for faster rebuilds.
 | 
				
			||||||
 | 
					                cacheDirectory: true,
 | 
				
			||||||
 | 
					                cacheCompression: isEnvProduction,
 | 
				
			||||||
 | 
					                compact: isEnvProduction,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Process any JS outside of the app with Babel.
 | 
				
			||||||
 | 
					            // Unlike the application JS, we only compile the standard ES features.
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: /\.(js|mjs)$/,
 | 
				
			||||||
 | 
					              exclude: /@babel(?:\/|\\{1,2})runtime/,
 | 
				
			||||||
 | 
					              loader: require.resolve("babel-loader"),
 | 
				
			||||||
 | 
					              options: {
 | 
				
			||||||
 | 
					                babelrc: false,
 | 
				
			||||||
 | 
					                configFile: false,
 | 
				
			||||||
 | 
					                compact: false,
 | 
				
			||||||
 | 
					                presets: [[require.resolve("babel-preset-react-app/dependencies"), {helpers: true}]],
 | 
				
			||||||
 | 
					                cacheDirectory: true,
 | 
				
			||||||
 | 
					                cacheCompression: isEnvProduction,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // If an error happens in a package, it's possible to be
 | 
				
			||||||
 | 
					                // because it was compiled. Thus, we don't want the browser
 | 
				
			||||||
 | 
					                // debugger to show the original code. Instead, the code
 | 
				
			||||||
 | 
					                // being evaluated would be much more helpful.
 | 
				
			||||||
 | 
					                sourceMaps: false,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // "postcss" loader applies autoprefixer to our CSS.
 | 
				
			||||||
 | 
					            // "css" loader resolves paths in CSS and adds assets as dependencies.
 | 
				
			||||||
 | 
					            // "style" loader turns CSS into JS modules that inject <style> tags.
 | 
				
			||||||
 | 
					            // In production, we use MiniCSSExtractPlugin to extract that CSS
 | 
				
			||||||
 | 
					            // to a file, but in development "style" loader enables hot editing
 | 
				
			||||||
 | 
					            // of CSS.
 | 
				
			||||||
 | 
					            // By default we support CSS Modules with the extension .module.css
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: cssRegex,
 | 
				
			||||||
 | 
					              exclude: cssModuleRegex,
 | 
				
			||||||
 | 
					              use: getStyleLoaders({
 | 
				
			||||||
 | 
					                importLoaders: 1,
 | 
				
			||||||
 | 
					                sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					              }),
 | 
				
			||||||
 | 
					              // Don't consider CSS imports dead code even if the
 | 
				
			||||||
 | 
					              // containing package claims to have no side effects.
 | 
				
			||||||
 | 
					              // Remove this when webpack adds a warning or an error for this.
 | 
				
			||||||
 | 
					              // See https://github.com/webpack/webpack/issues/6571
 | 
				
			||||||
 | 
					              sideEffects: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
 | 
				
			||||||
 | 
					            // using the extension .module.css
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: cssModuleRegex,
 | 
				
			||||||
 | 
					              use: getStyleLoaders({
 | 
				
			||||||
 | 
					                importLoaders: 1,
 | 
				
			||||||
 | 
					                sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					                modules: true,
 | 
				
			||||||
 | 
					                getLocalIdent: getCSSModuleLocalIdent,
 | 
				
			||||||
 | 
					              }),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Opt-in support for SASS (using .scss or .sass extensions).
 | 
				
			||||||
 | 
					            // By default we support SASS Modules with the
 | 
				
			||||||
 | 
					            // extensions .module.scss or .module.sass
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: sassRegex,
 | 
				
			||||||
 | 
					              exclude: sassModuleRegex,
 | 
				
			||||||
 | 
					              use: getStyleLoaders(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  importLoaders: 2,
 | 
				
			||||||
 | 
					                  sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "sass-loader",
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              // Don't consider CSS imports dead code even if the
 | 
				
			||||||
 | 
					              // containing package claims to have no side effects.
 | 
				
			||||||
 | 
					              // Remove this when webpack adds a warning or an error for this.
 | 
				
			||||||
 | 
					              // See https://github.com/webpack/webpack/issues/6571
 | 
				
			||||||
 | 
					              sideEffects: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Adds support for CSS Modules, but using SASS
 | 
				
			||||||
 | 
					            // using the extension .module.scss or .module.sass
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: sassModuleRegex,
 | 
				
			||||||
 | 
					              use: getStyleLoaders(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  importLoaders: 2,
 | 
				
			||||||
 | 
					                  sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					                  modules: true,
 | 
				
			||||||
 | 
					                  getLocalIdent: getCSSModuleLocalIdent,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "sass-loader",
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // "file" loader makes sure those assets get served by WebpackDevServer.
 | 
				
			||||||
 | 
					            // When you `import` an asset, you get its (virtual) filename.
 | 
				
			||||||
 | 
					            // In production, they would get copied to the `build` folder.
 | 
				
			||||||
 | 
					            // This loader doesn't use a "test" so it will catch all modules
 | 
				
			||||||
 | 
					            // that fall through the other loaders.
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              loader: require.resolve("file-loader"),
 | 
				
			||||||
 | 
					              // Exclude `js` files to keep "css" loader working as it injects
 | 
				
			||||||
 | 
					              // its runtime that would otherwise be processed through "file" loader.
 | 
				
			||||||
 | 
					              // Also exclude `html` and `json` extensions so they get processed
 | 
				
			||||||
 | 
					              // by webpacks internal loaders.
 | 
				
			||||||
 | 
					              exclude: [/\.(js|mjs|jsx|ts|tsx|md)$/, /\.html$/, /\.json$/],
 | 
				
			||||||
 | 
					              options: {
 | 
				
			||||||
 | 
					                name: "static/media/[name].[hash:8].[ext]",
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // ** STOP ** Are you adding a new loader?
 | 
				
			||||||
 | 
					            // Make sure to add the new loader(s) before the "file" loader.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              loader: require.resolve("raw-loader"),
 | 
				
			||||||
 | 
					              test: /\.md/i,
 | 
				
			||||||
 | 
					              include: path.resolve('./src/template')
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    plugins: [
 | 
				
			||||||
 | 
					      // Generates an `index.html` file with the <script> injected.
 | 
				
			||||||
 | 
					      new HtmlWebpackPlugin(
 | 
				
			||||||
 | 
					        Object.assign(
 | 
				
			||||||
 | 
					          {},
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            inject: true,
 | 
				
			||||||
 | 
					            template: paths.appHtml,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          isEnvProduction
 | 
				
			||||||
 | 
					            ? {
 | 
				
			||||||
 | 
					                minify: {
 | 
				
			||||||
 | 
					                  removeComments: true,
 | 
				
			||||||
 | 
					                  collapseWhitespace: true,
 | 
				
			||||||
 | 
					                  removeRedundantAttributes: true,
 | 
				
			||||||
 | 
					                  useShortDoctype: true,
 | 
				
			||||||
 | 
					                  removeEmptyAttributes: true,
 | 
				
			||||||
 | 
					                  removeStyleLinkTypeAttributes: true,
 | 
				
			||||||
 | 
					                  keepClosingSlash: true,
 | 
				
			||||||
 | 
					                  minifyJS: true,
 | 
				
			||||||
 | 
					                  minifyCSS: true,
 | 
				
			||||||
 | 
					                  minifyURLs: true,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            : undefined,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      // Inlines the webpack runtime script. This script is too small to warrant
 | 
				
			||||||
 | 
					      // a network request.
 | 
				
			||||||
 | 
					      isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
 | 
				
			||||||
 | 
					      // Makes some environment variables available in index.html.
 | 
				
			||||||
 | 
					      // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
 | 
				
			||||||
 | 
					      // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
 | 
				
			||||||
 | 
					      // In production, it will be an empty string unless you specify "homepage"
 | 
				
			||||||
 | 
					      // in `package.json`, in which case it will be the pathname of that URL.
 | 
				
			||||||
 | 
					      // In development, this will be an empty string.
 | 
				
			||||||
 | 
					      new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
 | 
				
			||||||
 | 
					      // This gives some necessary context to module not found errors, such as
 | 
				
			||||||
 | 
					      // the requesting resource.
 | 
				
			||||||
 | 
					      new ModuleNotFoundPlugin(paths.appPath),
 | 
				
			||||||
 | 
					      // Makes some environment variables available to the JS code, for example:
 | 
				
			||||||
 | 
					      // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
 | 
				
			||||||
 | 
					      // It is absolutely essential that NODE_ENV is set to production
 | 
				
			||||||
 | 
					      // during a production build.
 | 
				
			||||||
 | 
					      // Otherwise React will be compiled in the very slow development mode.
 | 
				
			||||||
 | 
					      new webpack.DefinePlugin(env.stringified),
 | 
				
			||||||
 | 
					      // This is necessary to emit hot updates (currently CSS only):
 | 
				
			||||||
 | 
					      isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
 | 
				
			||||||
 | 
					      // Watcher doesn't work well if you mistype casing in a path so we use
 | 
				
			||||||
 | 
					      // a plugin that prints an error when you attempt to do this.
 | 
				
			||||||
 | 
					      // See https://github.com/facebook/create-react-app/issues/240
 | 
				
			||||||
 | 
					      isEnvDevelopment && new CaseSensitivePathsPlugin(),
 | 
				
			||||||
 | 
					      // If you require a missing module and then `npm install` it, you still have
 | 
				
			||||||
 | 
					      // to restart the development server for Webpack to discover it. This plugin
 | 
				
			||||||
 | 
					      // makes the discovery automatic so you don't have to restart.
 | 
				
			||||||
 | 
					      // See https://github.com/facebook/create-react-app/issues/186
 | 
				
			||||||
 | 
					      isEnvDevelopment && new WatchMissingNodeModulesPlugin(paths.appNodeModules),
 | 
				
			||||||
 | 
					      isEnvProduction &&
 | 
				
			||||||
 | 
					        new MiniCssExtractPlugin({
 | 
				
			||||||
 | 
					          // Options similar to the same options in webpackOptions.output
 | 
				
			||||||
 | 
					          // both options are optional
 | 
				
			||||||
 | 
					          filename: "static/css/[name].[contenthash:8].css",
 | 
				
			||||||
 | 
					          chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      // Generate a manifest file which contains a mapping of all asset filenames
 | 
				
			||||||
 | 
					      // to their corresponding output file so that tools can pick it up without
 | 
				
			||||||
 | 
					      // having to parse `index.html`.
 | 
				
			||||||
 | 
					      new ManifestPlugin({
 | 
				
			||||||
 | 
					        fileName: "asset-manifest.json",
 | 
				
			||||||
 | 
					        publicPath: publicPath,
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
 | 
					      // Moment.js is an extremely popular library that bundles large locale files
 | 
				
			||||||
 | 
					      // by default due to how Webpack interprets its code. This is a practical
 | 
				
			||||||
 | 
					      // solution that requires the user to opt into importing specific locales.
 | 
				
			||||||
 | 
					      // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
 | 
				
			||||||
 | 
					      // You can remove this if you don't use Moment.js:
 | 
				
			||||||
 | 
					      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
 | 
				
			||||||
 | 
					      // Generate a service worker script that will precache, and keep up to date,
 | 
				
			||||||
 | 
					      // the HTML & assets that are part of the Webpack build.
 | 
				
			||||||
 | 
					      isEnvProduction &&
 | 
				
			||||||
 | 
					        new WorkboxWebpackPlugin.GenerateSW({
 | 
				
			||||||
 | 
					          clientsClaim: true,
 | 
				
			||||||
 | 
					          skipWaiting: true,
 | 
				
			||||||
 | 
					          exclude: [/\.map$/, /asset-manifest\.json$/],
 | 
				
			||||||
 | 
					          importWorkboxFrom: 'local',
 | 
				
			||||||
 | 
					          navigateFallback: publicUrl + '/index.html',
 | 
				
			||||||
 | 
					          navigateFallbackBlacklist: [
 | 
				
			||||||
 | 
					            // Exclude URLs starting with /_, as they're likely an API call
 | 
				
			||||||
 | 
					            new RegExp("^/_"),
 | 
				
			||||||
 | 
					            // Exclude URLs containing a dot, as they're likely a resource in
 | 
				
			||||||
 | 
					            // public/ and not a SPA route
 | 
				
			||||||
 | 
					            new RegExp("/[^/]+\\.[^/]+$"),
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          runtimeCaching: [
 | 
				
			||||||
 | 
					            // 配置路由请求缓存 对应 workbox.routing.registerRoute
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              urlPattern: /.*\.js/, // 匹配文件
 | 
				
			||||||
 | 
					              handler: "networkFirst", // 网络优先
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              urlPattern: /.*\.css/,
 | 
				
			||||||
 | 
					              handler: "staleWhileRevalidate", // 缓存优先同时后台更新
 | 
				
			||||||
 | 
					              options: {
 | 
				
			||||||
 | 
					                // 这里可以设置 cacheName 和添加插件
 | 
				
			||||||
 | 
					                plugins: [
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                    cacheableResponse: {
 | 
				
			||||||
 | 
					                      statuses: [0, 200],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              urlPattern: /.*\.(png|jpg|jpeg|svg|gif)/,
 | 
				
			||||||
 | 
					              handler: "cacheFirst", // 缓存优先
 | 
				
			||||||
 | 
					              options: {
 | 
				
			||||||
 | 
					                cacheName: 'images',
 | 
				
			||||||
 | 
					                expiration: {
 | 
				
			||||||
 | 
					                  maxAgeSeconds: 24 * 60 * 60, // 最长缓存时间,
 | 
				
			||||||
 | 
					                  maxEntries: 50, // 最大缓存图片数量
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              urlPattern: /.*\.html/,
 | 
				
			||||||
 | 
					              handler: "networkFirst",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      // TypeScript type checking
 | 
				
			||||||
 | 
					      useTypeScript &&
 | 
				
			||||||
 | 
					        new ForkTsCheckerWebpackPlugin({
 | 
				
			||||||
 | 
					          typescript: resolve.sync("typescript", {
 | 
				
			||||||
 | 
					            basedir: paths.appNodeModules,
 | 
				
			||||||
 | 
					          }),
 | 
				
			||||||
 | 
					          async: false,
 | 
				
			||||||
 | 
					          checkSyntacticErrors: true,
 | 
				
			||||||
 | 
					          tsconfig: paths.appTsConfig,
 | 
				
			||||||
 | 
					          compilerOptions: {
 | 
				
			||||||
 | 
					            module: "esnext",
 | 
				
			||||||
 | 
					            moduleResolution: "node",
 | 
				
			||||||
 | 
					            resolveJsonModule: true,
 | 
				
			||||||
 | 
					            isolatedModules: true,
 | 
				
			||||||
 | 
					            noEmit: true,
 | 
				
			||||||
 | 
					            jsx: "preserve",
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          reportFiles: [
 | 
				
			||||||
 | 
					            "**",
 | 
				
			||||||
 | 
					            "!**/*.json",
 | 
				
			||||||
 | 
					            "!**/__tests__/**",
 | 
				
			||||||
 | 
					            "!**/?(*.)(spec|test).*",
 | 
				
			||||||
 | 
					            "!**/src/setupProxy.*",
 | 
				
			||||||
 | 
					            "!**/src/setupTests.*",
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          watch: paths.appSrc,
 | 
				
			||||||
 | 
					          silent: true,
 | 
				
			||||||
 | 
					          formatter: typescriptFormatter,
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					    ].filter(Boolean),
 | 
				
			||||||
 | 
					    // Some libraries import Node modules but don't use them in the browser.
 | 
				
			||||||
 | 
					    // Tell Webpack to provide empty mocks for them so importing them works.
 | 
				
			||||||
 | 
					    node: {
 | 
				
			||||||
 | 
					      module: "empty",
 | 
				
			||||||
 | 
					      dgram: "empty",
 | 
				
			||||||
 | 
					      dns: "mock",
 | 
				
			||||||
 | 
					      fs: "empty",
 | 
				
			||||||
 | 
					      net: "empty",
 | 
				
			||||||
 | 
					      tls: "empty",
 | 
				
			||||||
 | 
					      child_process: "empty",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // Turn off performance processing because we utilize
 | 
				
			||||||
 | 
					    // our own hints via the FileSizeReporter
 | 
				
			||||||
 | 
					    performance: false,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										566
									
								
								config/webpack.config.lib.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										566
									
								
								config/webpack.config.lib.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,566 @@
 | 
				
			|||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fs = require("fs");
 | 
				
			||||||
 | 
					const path = require("path");
 | 
				
			||||||
 | 
					const webpack = require("webpack");
 | 
				
			||||||
 | 
					const resolve = require("resolve");
 | 
				
			||||||
 | 
					const PnpWebpackPlugin = require("pnp-webpack-plugin");
 | 
				
			||||||
 | 
					const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
 | 
				
			||||||
 | 
					const TerserPlugin = require("terser-webpack-plugin");
 | 
				
			||||||
 | 
					const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 | 
				
			||||||
 | 
					const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
 | 
				
			||||||
 | 
					const safePostCssParser = require("postcss-safe-parser");
 | 
				
			||||||
 | 
					const ManifestPlugin = require("webpack-manifest-plugin");
 | 
				
			||||||
 | 
					const WorkboxWebpackPlugin = require("workbox-webpack-plugin");
 | 
				
			||||||
 | 
					const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin");
 | 
				
			||||||
 | 
					const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
 | 
				
			||||||
 | 
					const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent");
 | 
				
			||||||
 | 
					const paths = require("./paths");
 | 
				
			||||||
 | 
					const getClientEnvironment = require("./env");
 | 
				
			||||||
 | 
					const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin");
 | 
				
			||||||
 | 
					const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin-alt");
 | 
				
			||||||
 | 
					const typescriptFormatter = require("react-dev-utils/typescriptFormatter");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Source maps are resource heavy and can cause out of memory issue for large source files.
 | 
				
			||||||
 | 
					const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
 | 
				
			||||||
 | 
					// Some apps do not need the benefits of saving a web request, so not inlining the chunk
 | 
				
			||||||
 | 
					// makes for a smoother build process.
 | 
				
			||||||
 | 
					const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check if TypeScript is setup
 | 
				
			||||||
 | 
					const useTypeScript = fs.existsSync(paths.appTsConfig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// style files regexes
 | 
				
			||||||
 | 
					const cssRegex = /\.css$/;
 | 
				
			||||||
 | 
					const cssModuleRegex = /\.module\.css$/;
 | 
				
			||||||
 | 
					const sassRegex = /\.(scss|sass)$/;
 | 
				
			||||||
 | 
					const sassModuleRegex = /\.module\.(scss|sass)$/;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the production and development configuration.
 | 
				
			||||||
 | 
					// It is focused on developer experience, fast rebuilds, and a minimal bundle.
 | 
				
			||||||
 | 
					module.exports = function(webpackEnv) {
 | 
				
			||||||
 | 
					  const isEnvDevelopment = webpackEnv === "development";
 | 
				
			||||||
 | 
					  const isEnvProduction = webpackEnv === "production";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Webpack uses `publicPath` to determine where the app is being served from.
 | 
				
			||||||
 | 
					  // It requires a trailing slash, or the file assets will get an incorrect path.
 | 
				
			||||||
 | 
					  // In development, we always serve from the root. This makes config easier.
 | 
				
			||||||
 | 
					  const publicPath = isEnvProduction ? paths.servedPath : isEnvDevelopment && "/";
 | 
				
			||||||
 | 
					  // Some apps do not use client-side routing with pushState.
 | 
				
			||||||
 | 
					  // For these, "homepage" can be set to "." to enable relative asset paths.
 | 
				
			||||||
 | 
					  const shouldUseRelativeAssetPaths = publicPath === "./";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // `publicUrl` is just like `publicPath`, but we will provide it to our app
 | 
				
			||||||
 | 
					  // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
 | 
				
			||||||
 | 
					  // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
 | 
				
			||||||
 | 
					  const publicUrl = isEnvProduction ? publicPath.slice(0, -1) : isEnvDevelopment && "";
 | 
				
			||||||
 | 
					  // Get environment variables to inject into our app.
 | 
				
			||||||
 | 
					  const env = getClientEnvironment(publicUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // common function to get style loaders
 | 
				
			||||||
 | 
					  const getStyleLoaders = (cssOptions, preProcessor) => {
 | 
				
			||||||
 | 
					    const loaders = [
 | 
				
			||||||
 | 
					      isEnvDevelopment && require.resolve("style-loader"),
 | 
				
			||||||
 | 
					      isEnvProduction && {
 | 
				
			||||||
 | 
					        loader: MiniCssExtractPlugin.loader,
 | 
				
			||||||
 | 
					        options: Object.assign({}, shouldUseRelativeAssetPaths ? {publicPath: "../../"} : undefined),
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        loader: require.resolve("css-loader"),
 | 
				
			||||||
 | 
					        options: cssOptions,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        // Options for PostCSS as we reference these options twice
 | 
				
			||||||
 | 
					        // Adds vendor prefixing based on your specified browser support in
 | 
				
			||||||
 | 
					        // package.json
 | 
				
			||||||
 | 
					        loader: require.resolve("postcss-loader"),
 | 
				
			||||||
 | 
					        options: {
 | 
				
			||||||
 | 
					          // Necessary for external CSS imports to work
 | 
				
			||||||
 | 
					          // https://github.com/facebook/create-react-app/issues/2677
 | 
				
			||||||
 | 
					          ident: "postcss",
 | 
				
			||||||
 | 
					          plugins: () => [
 | 
				
			||||||
 | 
					            require("postcss-flexbugs-fixes"),
 | 
				
			||||||
 | 
					            require("postcss-preset-env")({
 | 
				
			||||||
 | 
					              autoprefixer: {
 | 
				
			||||||
 | 
					                flexbox: "no-2009",
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              stage: 3,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ].filter(Boolean);
 | 
				
			||||||
 | 
					    if (preProcessor) {
 | 
				
			||||||
 | 
					      loaders.push({
 | 
				
			||||||
 | 
					        loader: require.resolve(preProcessor),
 | 
				
			||||||
 | 
					        options: {
 | 
				
			||||||
 | 
					          sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return loaders;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    mode: isEnvProduction ? "production" : isEnvDevelopment && "development",
 | 
				
			||||||
 | 
					    // Stop compilation early in production
 | 
				
			||||||
 | 
					    bail: isEnvProduction,
 | 
				
			||||||
 | 
					    devtool: isEnvProduction ? (shouldUseSourceMap ? "source-map" : false) : isEnvDevelopment && "eval-source-map",
 | 
				
			||||||
 | 
					    // These are the "entry points" to our application.
 | 
				
			||||||
 | 
					    // This means they will be the "root" imports that are included in JS bundle.
 | 
				
			||||||
 | 
					    entry: [
 | 
				
			||||||
 | 
					      // Include an alternative client for WebpackDevServer. A client's job is to
 | 
				
			||||||
 | 
					      // connect to WebpackDevServer by a socket and get notified about changes.
 | 
				
			||||||
 | 
					      // When you save a file, the client will either apply hot updates (in case
 | 
				
			||||||
 | 
					      // of CSS changes), or refresh the page (in case of JS changes). When you
 | 
				
			||||||
 | 
					      // make a syntax error, this client will display a syntax error overlay.
 | 
				
			||||||
 | 
					      // Note: instead of the default WebpackDevServer client, we use a custom one
 | 
				
			||||||
 | 
					      // to bring better experience for Create React App users. You can replace
 | 
				
			||||||
 | 
					      // the line below with these two lines if you prefer the stock client:
 | 
				
			||||||
 | 
					      // require.resolve('webpack-dev-server/client') + '?/',
 | 
				
			||||||
 | 
					      // require.resolve('webpack/hot/dev-server'),
 | 
				
			||||||
 | 
					      isEnvDevelopment && require.resolve("react-dev-utils/webpackHotDevClient"),
 | 
				
			||||||
 | 
					      // Finally, this is your app's code:
 | 
				
			||||||
 | 
					      paths.appLib,
 | 
				
			||||||
 | 
					      // We include the app code last so that if there is a runtime error during
 | 
				
			||||||
 | 
					      // initialization, it doesn't blow up the WebpackDevServer client, and
 | 
				
			||||||
 | 
					      // changing JS code would still trigger a refresh.
 | 
				
			||||||
 | 
					    ].filter(Boolean),
 | 
				
			||||||
 | 
					    output: {
 | 
				
			||||||
 | 
					      // The build folder.
 | 
				
			||||||
 | 
					      path: isEnvProduction ? paths.appBuild : undefined,
 | 
				
			||||||
 | 
					      // Add /* filename */ comments to generated require()s in the output.
 | 
				
			||||||
 | 
					      pathinfo: isEnvDevelopment,
 | 
				
			||||||
 | 
					      // There will be one main bundle, and one file per asynchronous chunk.
 | 
				
			||||||
 | 
					      // In development, it does not produce real files.
 | 
				
			||||||
 | 
					      filename: isEnvProduction ? "static/js/[name].[chunkhash:8].js" : isEnvDevelopment && "static/js/bundle.js",
 | 
				
			||||||
 | 
					      // There are also additional JS chunk files if you use code splitting.
 | 
				
			||||||
 | 
					      chunkFilename: isEnvProduction
 | 
				
			||||||
 | 
					        ? "static/js/[name].[chunkhash:8].chunk.js"
 | 
				
			||||||
 | 
					        : isEnvDevelopment && "static/js/[name].chunk.js",
 | 
				
			||||||
 | 
					      // We inferred the "public path" (such as / or /my-project) from homepage.
 | 
				
			||||||
 | 
					      // We use "/" in development.
 | 
				
			||||||
 | 
					      publicPath: publicPath,
 | 
				
			||||||
 | 
					      // Point sourcemap entries to original disk location (format as URL on Windows)
 | 
				
			||||||
 | 
					      devtoolModuleFilenameTemplate: isEnvProduction
 | 
				
			||||||
 | 
					        ? (info) => path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, "/")
 | 
				
			||||||
 | 
					        : isEnvDevelopment && ((info) => path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    optimization: {
 | 
				
			||||||
 | 
					      minimize: isEnvProduction,
 | 
				
			||||||
 | 
					      minimizer: [
 | 
				
			||||||
 | 
					        // This is only used in production mode
 | 
				
			||||||
 | 
					        new TerserPlugin({
 | 
				
			||||||
 | 
					          terserOptions: {
 | 
				
			||||||
 | 
					            parse: {
 | 
				
			||||||
 | 
					              // we want terser to parse ecma 8 code. However, we don't want it
 | 
				
			||||||
 | 
					              // to apply any minfication steps that turns valid ecma 5 code
 | 
				
			||||||
 | 
					              // into invalid ecma 5 code. This is why the 'compress' and 'output'
 | 
				
			||||||
 | 
					              // sections only apply transformations that are ecma 5 safe
 | 
				
			||||||
 | 
					              // https://github.com/facebook/create-react-app/pull/4234
 | 
				
			||||||
 | 
					              ecma: 8,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            compress: {
 | 
				
			||||||
 | 
					              ecma: 5,
 | 
				
			||||||
 | 
					              warnings: false,
 | 
				
			||||||
 | 
					              // Disabled because of an issue with Uglify breaking seemingly valid code:
 | 
				
			||||||
 | 
					              // https://github.com/facebook/create-react-app/issues/2376
 | 
				
			||||||
 | 
					              // Pending further investigation:
 | 
				
			||||||
 | 
					              // https://github.com/mishoo/UglifyJS2/issues/2011
 | 
				
			||||||
 | 
					              comparisons: false,
 | 
				
			||||||
 | 
					              // Disabled because of an issue with Terser breaking valid code:
 | 
				
			||||||
 | 
					              // https://github.com/facebook/create-react-app/issues/5250
 | 
				
			||||||
 | 
					              // Pending futher investigation:
 | 
				
			||||||
 | 
					              // https://github.com/terser-js/terser/issues/120
 | 
				
			||||||
 | 
					              inline: 2,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            mangle: {
 | 
				
			||||||
 | 
					              safari10: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            output: {
 | 
				
			||||||
 | 
					              ecma: 5,
 | 
				
			||||||
 | 
					              comments: false,
 | 
				
			||||||
 | 
					              // Turned on because emoji and regex is not minified properly using default
 | 
				
			||||||
 | 
					              // https://github.com/facebook/create-react-app/issues/2488
 | 
				
			||||||
 | 
					              ascii_only: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          // Use multi-process parallel running to improve the build speed
 | 
				
			||||||
 | 
					          // Default number of concurrent runs: os.cpus().length - 1
 | 
				
			||||||
 | 
					          parallel: true,
 | 
				
			||||||
 | 
					          // Enable file caching
 | 
				
			||||||
 | 
					          cache: true,
 | 
				
			||||||
 | 
					          sourceMap: shouldUseSourceMap,
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					        // This is only used in production mode
 | 
				
			||||||
 | 
					        new OptimizeCSSAssetsPlugin({
 | 
				
			||||||
 | 
					          cssProcessorOptions: {
 | 
				
			||||||
 | 
					            parser: safePostCssParser,
 | 
				
			||||||
 | 
					            map: shouldUseSourceMap
 | 
				
			||||||
 | 
					              ? {
 | 
				
			||||||
 | 
					                  // `inline: false` forces the sourcemap to be output into a
 | 
				
			||||||
 | 
					                  // separate file
 | 
				
			||||||
 | 
					                  inline: false,
 | 
				
			||||||
 | 
					                  // `annotation: true` appends the sourceMappingURL to the end of
 | 
				
			||||||
 | 
					                  // the css file, helping the browser find the sourcemap
 | 
				
			||||||
 | 
					                  annotation: true,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              : false,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      // Automatically split vendor and commons
 | 
				
			||||||
 | 
					      // https://twitter.com/wSokra/status/969633336732905474
 | 
				
			||||||
 | 
					      // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
 | 
				
			||||||
 | 
					      splitChunks: {
 | 
				
			||||||
 | 
					        chunks: "all",
 | 
				
			||||||
 | 
					        name: false,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      // Keep the runtime chunk separated to enable long term caching
 | 
				
			||||||
 | 
					      // https://twitter.com/wSokra/status/969679223278505985
 | 
				
			||||||
 | 
					      runtimeChunk: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    resolve: {
 | 
				
			||||||
 | 
					      // This allows you to set a fallback for where Webpack should look for modules.
 | 
				
			||||||
 | 
					      // We placed these paths second because we want `node_modules` to "win"
 | 
				
			||||||
 | 
					      // if there are any conflicts. This matches Node resolution mechanism.
 | 
				
			||||||
 | 
					      // https://github.com/facebook/create-react-app/issues/253
 | 
				
			||||||
 | 
					      modules: ["node_modules"].concat(
 | 
				
			||||||
 | 
					        // It is guaranteed to exist because we tweak it in `env.js`
 | 
				
			||||||
 | 
					        process.env.NODE_PATH.split(path.delimiter).filter(Boolean),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      // These are the reasonable defaults supported by the Node ecosystem.
 | 
				
			||||||
 | 
					      // We also include JSX as a common component filename extension to support
 | 
				
			||||||
 | 
					      // some tools, although we do not recommend using it, see:
 | 
				
			||||||
 | 
					      // https://github.com/facebook/create-react-app/issues/290
 | 
				
			||||||
 | 
					      // `web` extension prefixes have been added for better support
 | 
				
			||||||
 | 
					      // for React Native Web.
 | 
				
			||||||
 | 
					      extensions: paths.moduleFileExtensions
 | 
				
			||||||
 | 
					        .map((ext) => `.${ext}`)
 | 
				
			||||||
 | 
					        .filter((ext) => useTypeScript || !ext.includes("ts")),
 | 
				
			||||||
 | 
					      alias: {
 | 
				
			||||||
 | 
					        // Support React Native Web
 | 
				
			||||||
 | 
					        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
 | 
				
			||||||
 | 
					        "react-native": "react-native-web",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      plugins: [
 | 
				
			||||||
 | 
					        // Adds support for installing with Plug'n'Play, leading to faster installs and adding
 | 
				
			||||||
 | 
					        // guards against forgotten dependencies and such.
 | 
				
			||||||
 | 
					        PnpWebpackPlugin,
 | 
				
			||||||
 | 
					        // Prevents users from importing files from outside of src/ (or node_modules/).
 | 
				
			||||||
 | 
					        // This often causes confusion because we only process files within src/ with babel.
 | 
				
			||||||
 | 
					        // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
 | 
				
			||||||
 | 
					        // please link the files into your node_modules/ and let module-resolution kick in.
 | 
				
			||||||
 | 
					        // Make sure your source files are compiled, as they will not be processed in any way.
 | 
				
			||||||
 | 
					        new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    resolveLoader: {
 | 
				
			||||||
 | 
					      plugins: [
 | 
				
			||||||
 | 
					        // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
 | 
				
			||||||
 | 
					        // from the current package.
 | 
				
			||||||
 | 
					        PnpWebpackPlugin.moduleLoader(module),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    module: {
 | 
				
			||||||
 | 
					      strictExportPresence: true,
 | 
				
			||||||
 | 
					      rules: [
 | 
				
			||||||
 | 
					        // Disable require.ensure as it's not a standard language feature.
 | 
				
			||||||
 | 
					        {parser: {requireEnsure: false}},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // First, run the linter.
 | 
				
			||||||
 | 
					        // It's important to do this before Babel processes the JS.
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          // "oneOf" will traverse all following loaders until one will
 | 
				
			||||||
 | 
					          // match the requirements. When no loader matches it will fall
 | 
				
			||||||
 | 
					          // back to the "file" loader at the end of the loader list.
 | 
				
			||||||
 | 
					          oneOf: [
 | 
				
			||||||
 | 
					            // "url" loader works like "file" loader except that it embeds assets
 | 
				
			||||||
 | 
					            // smaller than specified limit in bytes as data URLs to avoid requests.
 | 
				
			||||||
 | 
					            // A missing `test` is equivalent to a match.
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
 | 
				
			||||||
 | 
					              loader: require.resolve("url-loader"),
 | 
				
			||||||
 | 
					              options: {
 | 
				
			||||||
 | 
					                limit: 10000,
 | 
				
			||||||
 | 
					                name: "static/media/[name].[hash:8].[ext]",
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Process application JS with Babel.
 | 
				
			||||||
 | 
					            // The preset includes JSX, Flow, TypeScript, and some ESnext features.
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: /\.(js|mjs|jsx|ts|tsx)$/,
 | 
				
			||||||
 | 
					              include: paths.appSrc,
 | 
				
			||||||
 | 
					              use: [
 | 
				
			||||||
 | 
					                "thread-loader",
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  loader: require.resolve("babel-loader"),
 | 
				
			||||||
 | 
					                  options: {
 | 
				
			||||||
 | 
					                    customize: require.resolve("babel-preset-react-app/webpack-overrides"),
 | 
				
			||||||
 | 
					                    plugins: [
 | 
				
			||||||
 | 
					                      [
 | 
				
			||||||
 | 
					                        require.resolve("babel-plugin-named-asset-import"),
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                          loaderMap: {
 | 
				
			||||||
 | 
					                            svg: {
 | 
				
			||||||
 | 
					                              ReactComponent: "@svgr/webpack?-svgo![path]",
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                          },
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                      ],
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    cacheDirectory: true,
 | 
				
			||||||
 | 
					                    cacheCompression: isEnvProduction,
 | 
				
			||||||
 | 
					                    compact: isEnvProduction,
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Process any JS outside of the app with Babel.
 | 
				
			||||||
 | 
					            // Unlike the application JS, we only compile the standard ES features.
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: /\.(js|mjs)$/,
 | 
				
			||||||
 | 
					              exclude: /@babel(?:\/|\\{1,2})runtime/,
 | 
				
			||||||
 | 
					              use: [
 | 
				
			||||||
 | 
					                "thread-loader",
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  loader: require.resolve("babel-loader"),
 | 
				
			||||||
 | 
					                  options: {
 | 
				
			||||||
 | 
					                    babelrc: false,
 | 
				
			||||||
 | 
					                    configFile: false,
 | 
				
			||||||
 | 
					                    compact: false,
 | 
				
			||||||
 | 
					                    presets: [[require.resolve("babel-preset-react-app/dependencies"), {helpers: true}]],
 | 
				
			||||||
 | 
					                    cacheDirectory: true,
 | 
				
			||||||
 | 
					                    cacheCompression: isEnvProduction,
 | 
				
			||||||
 | 
					                    sourceMaps: false,
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // "postcss" loader applies autoprefixer to our CSS.
 | 
				
			||||||
 | 
					            // "css" loader resolves paths in CSS and adds assets as dependencies.
 | 
				
			||||||
 | 
					            // "style" loader turns CSS into JS modules that inject <style> tags.
 | 
				
			||||||
 | 
					            // In production, we use MiniCSSExtractPlugin to extract that CSS
 | 
				
			||||||
 | 
					            // to a file, but in development "style" loader enables hot editing
 | 
				
			||||||
 | 
					            // of CSS.
 | 
				
			||||||
 | 
					            // By default we support CSS Modules with the extension .module.css
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: cssRegex,
 | 
				
			||||||
 | 
					              exclude: cssModuleRegex,
 | 
				
			||||||
 | 
					              use: getStyleLoaders({
 | 
				
			||||||
 | 
					                importLoaders: 1,
 | 
				
			||||||
 | 
					                sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					              }),
 | 
				
			||||||
 | 
					              // Don't consider CSS imports dead code even if the
 | 
				
			||||||
 | 
					              // containing package claims to have no side effects.
 | 
				
			||||||
 | 
					              // Remove this when webpack adds a warning or an error for this.
 | 
				
			||||||
 | 
					              // See https://github.com/webpack/webpack/issues/6571
 | 
				
			||||||
 | 
					              sideEffects: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
 | 
				
			||||||
 | 
					            // using the extension .module.css
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: cssModuleRegex,
 | 
				
			||||||
 | 
					              use: getStyleLoaders({
 | 
				
			||||||
 | 
					                importLoaders: 1,
 | 
				
			||||||
 | 
					                sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					                modules: true,
 | 
				
			||||||
 | 
					                getLocalIdent: getCSSModuleLocalIdent,
 | 
				
			||||||
 | 
					              }),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Opt-in support for SASS (using .scss or .sass extensions).
 | 
				
			||||||
 | 
					            // By default we support SASS Modules with the
 | 
				
			||||||
 | 
					            // extensions .module.scss or .module.sass
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: sassRegex,
 | 
				
			||||||
 | 
					              exclude: sassModuleRegex,
 | 
				
			||||||
 | 
					              use: getStyleLoaders(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  importLoaders: 2,
 | 
				
			||||||
 | 
					                  sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "sass-loader",
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              // Don't consider CSS imports dead code even if the
 | 
				
			||||||
 | 
					              // containing package claims to have no side effects.
 | 
				
			||||||
 | 
					              // Remove this when webpack adds a warning or an error for this.
 | 
				
			||||||
 | 
					              // See https://github.com/webpack/webpack/issues/6571
 | 
				
			||||||
 | 
					              sideEffects: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // Adds support for CSS Modules, but using SASS
 | 
				
			||||||
 | 
					            // using the extension .module.scss or .module.sass
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              test: sassModuleRegex,
 | 
				
			||||||
 | 
					              use: getStyleLoaders(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  importLoaders: 2,
 | 
				
			||||||
 | 
					                  sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
 | 
				
			||||||
 | 
					                  modules: true,
 | 
				
			||||||
 | 
					                  getLocalIdent: getCSSModuleLocalIdent,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "sass-loader",
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // "file" loader makes sure those assets get served by WebpackDevServer.
 | 
				
			||||||
 | 
					            // When you `import` an asset, you get its (virtual) filename.
 | 
				
			||||||
 | 
					            // In production, they would get copied to the `build` folder.
 | 
				
			||||||
 | 
					            // This loader doesn't use a "test" so it will catch all modules
 | 
				
			||||||
 | 
					            // that fall through the other loaders.
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              loader: require.resolve("file-loader"),
 | 
				
			||||||
 | 
					              // Exclude `js` files to keep "css" loader working as it injects
 | 
				
			||||||
 | 
					              // its runtime that would otherwise be processed through "file" loader.
 | 
				
			||||||
 | 
					              // Also exclude `html` and `json` extensions so they get processed
 | 
				
			||||||
 | 
					              // by webpacks internal loaders.
 | 
				
			||||||
 | 
					              exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
 | 
				
			||||||
 | 
					              options: {
 | 
				
			||||||
 | 
					                name: "static/media/[name].[hash:8].[ext]",
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // ** STOP ** Are you adding a new loader?
 | 
				
			||||||
 | 
					            // Make sure to add the new loader(s) before the "file" loader.
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    plugins: [
 | 
				
			||||||
 | 
					      // Generates an `index.html` file with the <script> injected.
 | 
				
			||||||
 | 
					      // new HtmlWebpackPlugin(
 | 
				
			||||||
 | 
					      //   Object.assign(
 | 
				
			||||||
 | 
					      //     {},
 | 
				
			||||||
 | 
					      //     {
 | 
				
			||||||
 | 
					      //       inject: true,
 | 
				
			||||||
 | 
					      //       template: paths.appHtml,
 | 
				
			||||||
 | 
					      //     },
 | 
				
			||||||
 | 
					      //     isEnvProduction
 | 
				
			||||||
 | 
					      //       ? {
 | 
				
			||||||
 | 
					      //           minify: {
 | 
				
			||||||
 | 
					      //             removeComments: true,
 | 
				
			||||||
 | 
					      //             collapseWhitespace: true,
 | 
				
			||||||
 | 
					      //             removeRedundantAttributes: true,
 | 
				
			||||||
 | 
					      //             useShortDoctype: true,
 | 
				
			||||||
 | 
					      //             removeEmptyAttributes: true,
 | 
				
			||||||
 | 
					      //             removeStyleLinkTypeAttributes: true,
 | 
				
			||||||
 | 
					      //             keepClosingSlash: true,
 | 
				
			||||||
 | 
					      //             minifyJS: true,
 | 
				
			||||||
 | 
					      //             minifyCSS: true,
 | 
				
			||||||
 | 
					      //             minifyURLs: true,
 | 
				
			||||||
 | 
					      //           },
 | 
				
			||||||
 | 
					      //         }
 | 
				
			||||||
 | 
					      //       : undefined,
 | 
				
			||||||
 | 
					      //   ),
 | 
				
			||||||
 | 
					      // ),
 | 
				
			||||||
 | 
					      // Inlines the webpack runtime script. This script is too small to warrant
 | 
				
			||||||
 | 
					      // a network request.
 | 
				
			||||||
 | 
					      // isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
 | 
				
			||||||
 | 
					      // Makes some environment variables available in index.html.
 | 
				
			||||||
 | 
					      // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
 | 
				
			||||||
 | 
					      // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
 | 
				
			||||||
 | 
					      // In production, it will be an empty string unless you specify "homepage"
 | 
				
			||||||
 | 
					      // in `package.json`, in which case it will be the pathname of that URL.
 | 
				
			||||||
 | 
					      // In development, this will be an empty string.
 | 
				
			||||||
 | 
					      // new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
 | 
				
			||||||
 | 
					      // This gives some necessary context to module not found errors, such as
 | 
				
			||||||
 | 
					      // the requesting resource.
 | 
				
			||||||
 | 
					      new ModuleNotFoundPlugin(paths.appPath),
 | 
				
			||||||
 | 
					      // Makes some environment variables available to the JS code, for example:
 | 
				
			||||||
 | 
					      // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
 | 
				
			||||||
 | 
					      // It is absolutely essential that NODE_ENV is set to production
 | 
				
			||||||
 | 
					      // during a production build.
 | 
				
			||||||
 | 
					      // Otherwise React will be compiled in the very slow development mode.
 | 
				
			||||||
 | 
					      new webpack.DefinePlugin(env.stringified),
 | 
				
			||||||
 | 
					      // This is necessary to emit hot updates (currently CSS only):
 | 
				
			||||||
 | 
					      // isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
 | 
				
			||||||
 | 
					      // Watcher doesn't work well if you mistype casing in a path so we use
 | 
				
			||||||
 | 
					      // a plugin that prints an error when you attempt to do this.
 | 
				
			||||||
 | 
					      // See https://github.com/facebook/create-react-app/issues/240
 | 
				
			||||||
 | 
					      isEnvDevelopment && new CaseSensitivePathsPlugin(),
 | 
				
			||||||
 | 
					      // If you require a missing module and then `npm install` it, you still have
 | 
				
			||||||
 | 
					      // to restart the development server for Webpack to discover it. This plugin
 | 
				
			||||||
 | 
					      // makes the discovery automatic so you don't have to restart.
 | 
				
			||||||
 | 
					      // See https://github.com/facebook/create-react-app/issues/186
 | 
				
			||||||
 | 
					      isEnvDevelopment && new WatchMissingNodeModulesPlugin(paths.appNodeModules),
 | 
				
			||||||
 | 
					      isEnvProduction &&
 | 
				
			||||||
 | 
					        new MiniCssExtractPlugin({
 | 
				
			||||||
 | 
					          // Options similar to the same options in webpackOptions.output
 | 
				
			||||||
 | 
					          // both options are optional
 | 
				
			||||||
 | 
					          filename: "static/css/[name].[contenthash:8].css",
 | 
				
			||||||
 | 
					          chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      // Generate a manifest file which contains a mapping of all asset filenames
 | 
				
			||||||
 | 
					      // to their corresponding output file so that tools can pick it up without
 | 
				
			||||||
 | 
					      // having to parse `index.html`.
 | 
				
			||||||
 | 
					      new ManifestPlugin({
 | 
				
			||||||
 | 
					        fileName: "asset-manifest.json",
 | 
				
			||||||
 | 
					        publicPath: publicPath,
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
 | 
					      // Moment.js is an extremely popular library that bundles large locale files
 | 
				
			||||||
 | 
					      // by default due to how Webpack interprets its code. This is a practical
 | 
				
			||||||
 | 
					      // solution that requires the user to opt into importing specific locales.
 | 
				
			||||||
 | 
					      // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
 | 
				
			||||||
 | 
					      // You can remove this if you don't use Moment.js:
 | 
				
			||||||
 | 
					      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
 | 
				
			||||||
 | 
					      // Generate a service worker script that will precache, and keep up to date,
 | 
				
			||||||
 | 
					      // the HTML & assets that are part of the Webpack build.
 | 
				
			||||||
 | 
					      isEnvProduction &&
 | 
				
			||||||
 | 
					        new WorkboxWebpackPlugin.GenerateSW({
 | 
				
			||||||
 | 
					          clientsClaim: true,
 | 
				
			||||||
 | 
					          exclude: [/\.map$/, /asset-manifest\.json$/],
 | 
				
			||||||
 | 
					          importWorkboxFrom: "cdn",
 | 
				
			||||||
 | 
					          navigateFallback: publicUrl + "/index.html",
 | 
				
			||||||
 | 
					          navigateFallbackBlacklist: [
 | 
				
			||||||
 | 
					            // Exclude URLs starting with /_, as they're likely an API call
 | 
				
			||||||
 | 
					            new RegExp("^/_"),
 | 
				
			||||||
 | 
					            // Exclude URLs containing a dot, as they're likely a resource in
 | 
				
			||||||
 | 
					            // public/ and not a SPA route
 | 
				
			||||||
 | 
					            new RegExp("/[^/]+\\.[^/]+$"),
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      // TypeScript type checking
 | 
				
			||||||
 | 
					      useTypeScript &&
 | 
				
			||||||
 | 
					        new ForkTsCheckerWebpackPlugin({
 | 
				
			||||||
 | 
					          typescript: resolve.sync("typescript", {
 | 
				
			||||||
 | 
					            basedir: paths.appNodeModules,
 | 
				
			||||||
 | 
					          }),
 | 
				
			||||||
 | 
					          async: false,
 | 
				
			||||||
 | 
					          checkSyntacticErrors: true,
 | 
				
			||||||
 | 
					          tsconfig: paths.appTsConfig,
 | 
				
			||||||
 | 
					          compilerOptions: {
 | 
				
			||||||
 | 
					            module: "esnext",
 | 
				
			||||||
 | 
					            moduleResolution: "node",
 | 
				
			||||||
 | 
					            resolveJsonModule: true,
 | 
				
			||||||
 | 
					            isolatedModules: true,
 | 
				
			||||||
 | 
					            noEmit: true,
 | 
				
			||||||
 | 
					            jsx: "preserve",
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          reportFiles: [
 | 
				
			||||||
 | 
					            "**",
 | 
				
			||||||
 | 
					            "!**/*.json",
 | 
				
			||||||
 | 
					            "!**/__tests__/**",
 | 
				
			||||||
 | 
					            "!**/?(*.)(spec|test).*",
 | 
				
			||||||
 | 
					            "!**/src/setupProxy.*",
 | 
				
			||||||
 | 
					            "!**/src/setupTests.*",
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          watch: paths.appSrc,
 | 
				
			||||||
 | 
					          silent: true,
 | 
				
			||||||
 | 
					          formatter: typescriptFormatter,
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					    ].filter(Boolean),
 | 
				
			||||||
 | 
					    // Some libraries import Node modules but don't use them in the browser.
 | 
				
			||||||
 | 
					    // Tell Webpack to provide empty mocks for them so importing them works.
 | 
				
			||||||
 | 
					    node: {
 | 
				
			||||||
 | 
					      module: "empty",
 | 
				
			||||||
 | 
					      dgram: "empty",
 | 
				
			||||||
 | 
					      dns: "mock",
 | 
				
			||||||
 | 
					      fs: "empty",
 | 
				
			||||||
 | 
					      net: "empty",
 | 
				
			||||||
 | 
					      tls: "empty",
 | 
				
			||||||
 | 
					      child_process: "empty",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // Turn off performance processing because we utilize
 | 
				
			||||||
 | 
					    // our own hints via the FileSizeReporter
 | 
				
			||||||
 | 
					    performance: false,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										104
									
								
								config/webpackDevServer.config.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										104
									
								
								config/webpackDevServer.config.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
 | 
				
			||||||
 | 
					const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
 | 
				
			||||||
 | 
					const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
 | 
				
			||||||
 | 
					const ignoredFiles = require('react-dev-utils/ignoredFiles');
 | 
				
			||||||
 | 
					const paths = require('./paths');
 | 
				
			||||||
 | 
					const fs = require('fs');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
 | 
				
			||||||
 | 
					const host = process.env.HOST || '0.0.0.0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = function(proxy, allowedHost) {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    // WebpackDevServer 2.4.3 introduced a security fix that prevents remote
 | 
				
			||||||
 | 
					    // websites from potentially accessing local content through DNS rebinding:
 | 
				
			||||||
 | 
					    // https://github.com/webpack/webpack-dev-server/issues/887
 | 
				
			||||||
 | 
					    // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
 | 
				
			||||||
 | 
					    // However, it made several existing use cases such as development in cloud
 | 
				
			||||||
 | 
					    // environment or subdomains in development significantly more complicated:
 | 
				
			||||||
 | 
					    // https://github.com/facebook/create-react-app/issues/2271
 | 
				
			||||||
 | 
					    // https://github.com/facebook/create-react-app/issues/2233
 | 
				
			||||||
 | 
					    // While we're investigating better solutions, for now we will take a
 | 
				
			||||||
 | 
					    // compromise. Since our WDS configuration only serves files in the `public`
 | 
				
			||||||
 | 
					    // folder we won't consider accessing them a vulnerability. However, if you
 | 
				
			||||||
 | 
					    // use the `proxy` feature, it gets more dangerous because it can expose
 | 
				
			||||||
 | 
					    // remote code execution vulnerabilities in backends like Django and Rails.
 | 
				
			||||||
 | 
					    // So we will disable the host check normally, but enable it if you have
 | 
				
			||||||
 | 
					    // specified the `proxy` setting. Finally, we let you override it if you
 | 
				
			||||||
 | 
					    // really know what you're doing with a special environment variable.
 | 
				
			||||||
 | 
					    disableHostCheck:
 | 
				
			||||||
 | 
					      !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
 | 
				
			||||||
 | 
					    // Enable gzip compression of generated files.
 | 
				
			||||||
 | 
					    compress: true,
 | 
				
			||||||
 | 
					    // Silence WebpackDevServer's own logs since they're generally not useful.
 | 
				
			||||||
 | 
					    // It will still show compile warnings and errors with this setting.
 | 
				
			||||||
 | 
					    clientLogLevel: 'none',
 | 
				
			||||||
 | 
					    // By default WebpackDevServer serves physical files from current directory
 | 
				
			||||||
 | 
					    // in addition to all the virtual build products that it serves from memory.
 | 
				
			||||||
 | 
					    // This is confusing because those files won’t automatically be available in
 | 
				
			||||||
 | 
					    // production build folder unless we copy them. However, copying the whole
 | 
				
			||||||
 | 
					    // project directory is dangerous because we may expose sensitive files.
 | 
				
			||||||
 | 
					    // Instead, we establish a convention that only files in `public` directory
 | 
				
			||||||
 | 
					    // get served. Our build script will copy `public` into the `build` folder.
 | 
				
			||||||
 | 
					    // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
 | 
				
			||||||
 | 
					    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
 | 
				
			||||||
 | 
					    // In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
 | 
				
			||||||
 | 
					    // Note that we only recommend to use `public` folder as an escape hatch
 | 
				
			||||||
 | 
					    // for files like `favicon.ico`, `manifest.json`, and libraries that are
 | 
				
			||||||
 | 
					    // for some reason broken when imported through Webpack. If you just want to
 | 
				
			||||||
 | 
					    // use an image, put it in `src` and `import` it from JavaScript instead.
 | 
				
			||||||
 | 
					    contentBase: paths.appPublic,
 | 
				
			||||||
 | 
					    // By default files from `contentBase` will not trigger a page reload.
 | 
				
			||||||
 | 
					    watchContentBase: true,
 | 
				
			||||||
 | 
					    // Enable hot reloading server. It will provide /sockjs-node/ endpoint
 | 
				
			||||||
 | 
					    // for the WebpackDevServer client so it can learn when the files were
 | 
				
			||||||
 | 
					    // updated. The WebpackDevServer client is included as an entry point
 | 
				
			||||||
 | 
					    // in the Webpack development configuration. Note that only changes
 | 
				
			||||||
 | 
					    // to CSS are currently hot reloaded. JS changes will refresh the browser.
 | 
				
			||||||
 | 
					    hot: true,
 | 
				
			||||||
 | 
					    // It is important to tell WebpackDevServer to use the same "root" path
 | 
				
			||||||
 | 
					    // as we specified in the config. In development, we always serve from /.
 | 
				
			||||||
 | 
					    publicPath: '/',
 | 
				
			||||||
 | 
					    // WebpackDevServer is noisy by default so we emit custom message instead
 | 
				
			||||||
 | 
					    // by listening to the compiler events with `compiler.hooks[...].tap` calls above.
 | 
				
			||||||
 | 
					    quiet: true,
 | 
				
			||||||
 | 
					    // Reportedly, this avoids CPU overload on some systems.
 | 
				
			||||||
 | 
					    // https://github.com/facebook/create-react-app/issues/293
 | 
				
			||||||
 | 
					    // src/node_modules is not ignored to support absolute imports
 | 
				
			||||||
 | 
					    // https://github.com/facebook/create-react-app/issues/1065
 | 
				
			||||||
 | 
					    watchOptions: {
 | 
				
			||||||
 | 
					      ignored: ignoredFiles(paths.appSrc),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // Enable HTTPS if the HTTPS environment variable is set to 'true'
 | 
				
			||||||
 | 
					    https: protocol === 'https',
 | 
				
			||||||
 | 
					    host,
 | 
				
			||||||
 | 
					    overlay: false,
 | 
				
			||||||
 | 
					    historyApiFallback: {
 | 
				
			||||||
 | 
					      // Paths with dots should still use the history fallback.
 | 
				
			||||||
 | 
					      // See https://github.com/facebook/create-react-app/issues/387.
 | 
				
			||||||
 | 
					      disableDotRule: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    public: allowedHost,
 | 
				
			||||||
 | 
					    proxy,
 | 
				
			||||||
 | 
					    before(app, server) {
 | 
				
			||||||
 | 
					      if (fs.existsSync(paths.proxySetup)) {
 | 
				
			||||||
 | 
					        // This registers user provided middleware for proxy reasons
 | 
				
			||||||
 | 
					        require(paths.proxySetup)(app);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // This lets us fetch source contents from webpack for the error overlay
 | 
				
			||||||
 | 
					      app.use(evalSourceMapMiddleware(server));
 | 
				
			||||||
 | 
					      // This lets us open files from the runtime error overlay.
 | 
				
			||||||
 | 
					      app.use(errorOverlayMiddleware());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // This service worker file is effectively a 'no-op' that will reset any
 | 
				
			||||||
 | 
					      // previous service worker registered for the same host:port combination.
 | 
				
			||||||
 | 
					      // We do this in development to avoid hitting the production cache if
 | 
				
			||||||
 | 
					      // it used the same host and port.
 | 
				
			||||||
 | 
					      // https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
 | 
				
			||||||
 | 
					      app.use(noopServiceWorkerMiddleware());
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										49
									
								
								main.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										49
									
								
								main.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					// 引入electron并创建一个Browserwindow
 | 
				
			||||||
 | 
					const {app, BrowserWindow} = require('electron')
 | 
				
			||||||
 | 
					const path = require('path')
 | 
				
			||||||
 | 
					const url = require('url')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 保持window对象的全局引用,避免JavaScript对象被垃圾回收时,窗口被自动关闭.
 | 
				
			||||||
 | 
					let mainWindow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function createWindow () {
 | 
				
			||||||
 | 
					//创建浏览器窗口,宽高自定义具体大小你开心就好
 | 
				
			||||||
 | 
					mainWindow = new BrowserWindow({width: 800, height: 600})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 加载应用----react 打包
 | 
				
			||||||
 | 
					  mainWindow.loadURL(url.format({
 | 
				
			||||||
 | 
					    pathname: path.join(__dirname, './build/index.html'),
 | 
				
			||||||
 | 
					    protocol: 'file:',
 | 
				
			||||||
 | 
					    slashes: true
 | 
				
			||||||
 | 
					  }))
 | 
				
			||||||
 | 
					  // 加载应用----适用于 react 开发时项目
 | 
				
			||||||
 | 
					  // mainWindow.loadURL('http://localhost:3000/');
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // 打开开发者工具,默认不打开
 | 
				
			||||||
 | 
					  // mainWindow.webContents.openDevTools()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 关闭window时触发下列事件.
 | 
				
			||||||
 | 
					  mainWindow.on('closed', function () {
 | 
				
			||||||
 | 
					    mainWindow = null
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 当 Electron 完成初始化并准备创建浏览器窗口时调用此方法
 | 
				
			||||||
 | 
					app.on('ready', createWindow)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 所有窗口关闭时退出应用.
 | 
				
			||||||
 | 
					app.on('window-all-closed', function () {
 | 
				
			||||||
 | 
					  // macOS中除非用户按下 `Cmd + Q` 显式退出,否则应用与菜单栏始终处于活动状态.
 | 
				
			||||||
 | 
					  if (process.platform !== 'darwin') {
 | 
				
			||||||
 | 
					    app.quit()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.on('activate', function () {
 | 
				
			||||||
 | 
					   // macOS中点击Dock图标时没有已打开的其余应用窗口时,则通常在应用中重建一个窗口
 | 
				
			||||||
 | 
					  if (mainWindow === null) {
 | 
				
			||||||
 | 
					    createWindow()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 你可以在这个脚本中续写或者使用require引入独立的js文件.
 | 
				
			||||||
							
								
								
									
										242
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										242
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,242 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "markdown2wechat",
 | 
				
			||||||
 | 
					  "author": "tale",
 | 
				
			||||||
 | 
					  "description": "a markdown editor with the function of style edition",
 | 
				
			||||||
 | 
					  "version": "1.0.0",
 | 
				
			||||||
 | 
					  "private": false,
 | 
				
			||||||
 | 
					  "main": "lib/Lib.js",
 | 
				
			||||||
 | 
					  "module": "lib/Lib.js",
 | 
				
			||||||
 | 
					  "homepage": "http://md.aizhuanqian.online",
 | 
				
			||||||
 | 
					  "license": "GPL-3.0",
 | 
				
			||||||
 | 
					  "typings": "./lib/index.d.ts",
 | 
				
			||||||
 | 
					  "repository": {
 | 
				
			||||||
 | 
					    "type": "git",
 | 
				
			||||||
 | 
					    "url": "https://github.com/TaleAi/markdown2html"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "start": "node scripts/start.js",
 | 
				
			||||||
 | 
					    "watch": "node ./watch.js",
 | 
				
			||||||
 | 
					    "build": "export GENERATE_SOURCEMAP=false&&node --max_old_space_size=4096 scripts/build.js",
 | 
				
			||||||
 | 
					    "test": "node scripts/test.js",
 | 
				
			||||||
 | 
					    "analyze": "source-map-explorer build/static/js/*.js",
 | 
				
			||||||
 | 
					    "lint": "eslint src --ext ts,tsx,js --fix",
 | 
				
			||||||
 | 
					    "publish:npm": "cross-env NODE_ENV=production && rm -rf lib && mkdir lib && cross-env BABEL_ENV=production npx babel src --out-dir lib --copy-files",
 | 
				
			||||||
 | 
					    "storybook": "npm run publish:npm && start-storybook -p 9001 -c .storybook"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@sitdown/juejin": "^1.1.1",
 | 
				
			||||||
 | 
					    "@sitdown/wechat": "^1.1.4",
 | 
				
			||||||
 | 
					    "@sitdown/zhihu": "^1.1.2",
 | 
				
			||||||
 | 
					    "@uiw/react-codemirror": "^1.0.28",
 | 
				
			||||||
 | 
					    "ali-oss": "^6.1.1",
 | 
				
			||||||
 | 
					    "antd": "^3.15.1",
 | 
				
			||||||
 | 
					    "axios": "^0.18.0",
 | 
				
			||||||
 | 
					    "diff-match-patch": "^1.0.4",
 | 
				
			||||||
 | 
					    "highlight.js": "^9.15.6",
 | 
				
			||||||
 | 
					    "juice": "^5.2.0",
 | 
				
			||||||
 | 
					    "lodash.debounce": "^4.0.8",
 | 
				
			||||||
 | 
					    "lodash.throttle": "^4.1.1",
 | 
				
			||||||
 | 
					    "markdown-it": "^8.4.2",
 | 
				
			||||||
 | 
					    "markdown-it-deflist": "^2.0.3",
 | 
				
			||||||
 | 
					    "markdown-it-footnote": "^3.0.1",
 | 
				
			||||||
 | 
					    "markdown-it-implicit-figures": "^0.9.0",
 | 
				
			||||||
 | 
					    "markdown-it-imsize": "^2.0.1",
 | 
				
			||||||
 | 
					    "markdown-it-katex": "^2.0.3",
 | 
				
			||||||
 | 
					    "markdown-it-ruby": "^0.1.1",
 | 
				
			||||||
 | 
					    "markdown-it-table-of-contents": "^0.4.4",
 | 
				
			||||||
 | 
					    "mathjax": "^3.0.1",
 | 
				
			||||||
 | 
					    "mobx": "^5.9.0",
 | 
				
			||||||
 | 
					    "mobx-react": "^5.4.3",
 | 
				
			||||||
 | 
					    "prettier": "^1.19.1",
 | 
				
			||||||
 | 
					    "qiniu-js": "^2.5.4",
 | 
				
			||||||
 | 
					    "react": "16.10.2",
 | 
				
			||||||
 | 
					    "react-dom": "16.10.2",
 | 
				
			||||||
 | 
					    "react-helmet": "^5.2.1",
 | 
				
			||||||
 | 
					    "sitdown": "^1.1.3"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@babel/cli": "^7.6.2",
 | 
				
			||||||
 | 
					    "@babel/core": "7.2.2",
 | 
				
			||||||
 | 
					    "@storybook/react": "^4.1.11",
 | 
				
			||||||
 | 
					    "@svgr/webpack": "^4.1.0",
 | 
				
			||||||
 | 
					    "babel-core": "7.0.0-bridge.0",
 | 
				
			||||||
 | 
					    "babel-eslint": "^9.0.0",
 | 
				
			||||||
 | 
					    "babel-jest": "23.6.0",
 | 
				
			||||||
 | 
					    "babel-loader": "8.0.5",
 | 
				
			||||||
 | 
					    "babel-plugin-inline-import": "^3.0.0",
 | 
				
			||||||
 | 
					    "babel-plugin-named-asset-import": "^0.3.1",
 | 
				
			||||||
 | 
					    "babel-preset-react-app": "^7.0.1",
 | 
				
			||||||
 | 
					    "bfj": "6.1.1",
 | 
				
			||||||
 | 
					    "case-sensitive-paths-webpack-plugin": "2.2.0",
 | 
				
			||||||
 | 
					    "chalk": "^2.4.2",
 | 
				
			||||||
 | 
					    "chokidar": "^3.2.1",
 | 
				
			||||||
 | 
					    "cross-env": "^6.0.3",
 | 
				
			||||||
 | 
					    "css-loader": "^2.1.1",
 | 
				
			||||||
 | 
					    "dotenv": "6.0.0",
 | 
				
			||||||
 | 
					    "dotenv-expand": "4.2.0",
 | 
				
			||||||
 | 
					    "eslint": "^6.5.0",
 | 
				
			||||||
 | 
					    "eslint-config-airbnb": "^18.0.1",
 | 
				
			||||||
 | 
					    "eslint-config-prettier": "^6.3.0",
 | 
				
			||||||
 | 
					    "eslint-config-react-app": "^3.0.7",
 | 
				
			||||||
 | 
					    "eslint-loader": "^2.1.1",
 | 
				
			||||||
 | 
					    "eslint-plugin-babel": "^5.3.0",
 | 
				
			||||||
 | 
					    "eslint-plugin-flowtype": "2.50.1",
 | 
				
			||||||
 | 
					    "eslint-plugin-import": "^2.14.0",
 | 
				
			||||||
 | 
					    "eslint-plugin-jsx-a11y": "^6.1.2",
 | 
				
			||||||
 | 
					    "eslint-plugin-prettier": "^3.1.1",
 | 
				
			||||||
 | 
					    "eslint-plugin-react": "^7.12.4",
 | 
				
			||||||
 | 
					    "file-loader": "2.0.0",
 | 
				
			||||||
 | 
					    "fork-ts-checker-webpack-plugin-alt": "0.4.14",
 | 
				
			||||||
 | 
					    "fs-extra": "7.0.1",
 | 
				
			||||||
 | 
					    "html-webpack-plugin": "4.0.0-alpha.2",
 | 
				
			||||||
 | 
					    "husky": "^3.0.7",
 | 
				
			||||||
 | 
					    "identity-obj-proxy": "3.0.0",
 | 
				
			||||||
 | 
					    "jest": "23.6.0",
 | 
				
			||||||
 | 
					    "jest-pnp-resolver": "1.0.2",
 | 
				
			||||||
 | 
					    "jest-resolve": "23.6.0",
 | 
				
			||||||
 | 
					    "jest-watch-typeahead": "^0.2.1",
 | 
				
			||||||
 | 
					    "lint-staged": "^9.4.0",
 | 
				
			||||||
 | 
					    "mini-css-extract-plugin": "0.5.0",
 | 
				
			||||||
 | 
					    "optimize-css-assets-webpack-plugin": "5.0.1",
 | 
				
			||||||
 | 
					    "pnp-webpack-plugin": "1.2.1",
 | 
				
			||||||
 | 
					    "postcss-flexbugs-fixes": "4.1.0",
 | 
				
			||||||
 | 
					    "postcss-loader": "3.0.0",
 | 
				
			||||||
 | 
					    "postcss-preset-env": "6.5.0",
 | 
				
			||||||
 | 
					    "postcss-safe-parser": "4.0.1",
 | 
				
			||||||
 | 
					    "pretty-quick": "^1.11.1",
 | 
				
			||||||
 | 
					    "raw-loader": "^4.0.0",
 | 
				
			||||||
 | 
					    "react-app-polyfill": "^0.2.1",
 | 
				
			||||||
 | 
					    "react-dev-utils": "^7.0.3",
 | 
				
			||||||
 | 
					    "resolve": "1.10.0",
 | 
				
			||||||
 | 
					    "sass-loader": "7.1.0",
 | 
				
			||||||
 | 
					    "shelljs": "^0.8.3",
 | 
				
			||||||
 | 
					    "source-map-explorer": "^2.0.1",
 | 
				
			||||||
 | 
					    "style-loader": "0.23.1",
 | 
				
			||||||
 | 
					    "styled-jsx": "^3.2.1",
 | 
				
			||||||
 | 
					    "terser-webpack-plugin": "1.2.2",
 | 
				
			||||||
 | 
					    "thread-loader": "^2.1.3",
 | 
				
			||||||
 | 
					    "to-string-loader": "^1.1.5",
 | 
				
			||||||
 | 
					    "url-loader": "1.1.2",
 | 
				
			||||||
 | 
					    "webpack": "4.28.3",
 | 
				
			||||||
 | 
					    "webpack-dev-server": "3.1.14",
 | 
				
			||||||
 | 
					    "webpack-manifest-plugin": "2.0.4",
 | 
				
			||||||
 | 
					    "workbox-webpack-plugin": "3.6.3"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "build": {
 | 
				
			||||||
 | 
					    "productName": "Markdown2Html",
 | 
				
			||||||
 | 
					    "appId": "com.aizhuanqian.www",
 | 
				
			||||||
 | 
					    "mac": {
 | 
				
			||||||
 | 
					      "target": [
 | 
				
			||||||
 | 
					        "dmg",
 | 
				
			||||||
 | 
					        "zip"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "win": {
 | 
				
			||||||
 | 
					      "target": [
 | 
				
			||||||
 | 
					        "nsis",
 | 
				
			||||||
 | 
					        "zip"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "files": [
 | 
				
			||||||
 | 
					      "build",
 | 
				
			||||||
 | 
					      "main.js",
 | 
				
			||||||
 | 
					      "package.json"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "eslintConfig": {
 | 
				
			||||||
 | 
					    "extends": "react-app"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "browserslist": [
 | 
				
			||||||
 | 
					    ">0.2%",
 | 
				
			||||||
 | 
					    "not dead",
 | 
				
			||||||
 | 
					    "not ie <= 11",
 | 
				
			||||||
 | 
					    "not op_mini all"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "jest": {
 | 
				
			||||||
 | 
					    "collectCoverageFrom": [
 | 
				
			||||||
 | 
					      "src/**/*.{js,jsx,ts,tsx}",
 | 
				
			||||||
 | 
					      "!src/**/*.d.ts"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "resolver": "jest-pnp-resolver",
 | 
				
			||||||
 | 
					    "setupFiles": [
 | 
				
			||||||
 | 
					      "react-app-polyfill/jsdom"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "testMatch": [
 | 
				
			||||||
 | 
					      "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
 | 
				
			||||||
 | 
					      "<rootDir>/src/**/?(*.)(spec|test).{js,jsx,ts,tsx}"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "testEnvironment": "jsdom",
 | 
				
			||||||
 | 
					    "testURL": "http://localhost",
 | 
				
			||||||
 | 
					    "transform": {
 | 
				
			||||||
 | 
					      "^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
 | 
				
			||||||
 | 
					      "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
 | 
				
			||||||
 | 
					      "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "transformIgnorePatterns": [
 | 
				
			||||||
 | 
					      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
 | 
				
			||||||
 | 
					      "^.+\\.module\\.(css|sass|scss)$"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "moduleNameMapper": {
 | 
				
			||||||
 | 
					      "^react-native$": "react-native-web",
 | 
				
			||||||
 | 
					      "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "moduleFileExtensions": [
 | 
				
			||||||
 | 
					      "web.js",
 | 
				
			||||||
 | 
					      "js",
 | 
				
			||||||
 | 
					      "web.ts",
 | 
				
			||||||
 | 
					      "ts",
 | 
				
			||||||
 | 
					      "web.tsx",
 | 
				
			||||||
 | 
					      "tsx",
 | 
				
			||||||
 | 
					      "json",
 | 
				
			||||||
 | 
					      "web.jsx",
 | 
				
			||||||
 | 
					      "jsx",
 | 
				
			||||||
 | 
					      "node"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "watchPlugins": []
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "babel": {
 | 
				
			||||||
 | 
					    "presets": [
 | 
				
			||||||
 | 
					      "@babel/react"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "plugins": [
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        "@babel/plugin-proposal-decorators",
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "legacy": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        "@babel/plugin-proposal-class-properties",
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "loose": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "env": {
 | 
				
			||||||
 | 
					      "production": {
 | 
				
			||||||
 | 
					        "plugins": [
 | 
				
			||||||
 | 
					          [
 | 
				
			||||||
 | 
					            "babel-plugin-inline-import",
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "extensions": [
 | 
				
			||||||
 | 
					                ".md"
 | 
				
			||||||
 | 
					              ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "husky": {
 | 
				
			||||||
 | 
					    "hooks": {
 | 
				
			||||||
 | 
					      "pre-commit": "lint-staged"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "lint-staged": {
 | 
				
			||||||
 | 
					    "src/**/*.{jsx,txs,ts,js,json}": [
 | 
				
			||||||
 | 
					      "prettier --write",
 | 
				
			||||||
 | 
					      "eslint --fix",
 | 
				
			||||||
 | 
					      "git add"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								public/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										16
									
								
								public/favicon.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" version="1.1" width="400" height="400" style="">
 | 
				
			||||||
 | 
					    <rect width="100%" height="100%" fill="black" rx="20%" ry="20%" />
 | 
				
			||||||
 | 
					    <g fill="#ffffff" transform="translate(300, 100)">
 | 
				
			||||||
 | 
					        <g transform="scale(16)">
 | 
				
			||||||
 | 
					            <svg filter="url(#colors2300178586)" x="0" y="0" width="27.442663140478597" height="50">
 | 
				
			||||||
 | 
					                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17.47 31.83">
 | 
				
			||||||
 | 
					                    <g fill="#ffffff">
 | 
				
			||||||
 | 
					                        <path d="M0 31.83V0h.81v31.02h16.66v.81H0z"/>
 | 
				
			||||||
 | 
					                        <path d="M6.69 26.11V0h3.24v22.87h7.54v3.24H6.69z"/>
 | 
				
			||||||
 | 
					                    </g>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					            </svg>
 | 
				
			||||||
 | 
					        </g>
 | 
				
			||||||
 | 
					    </g>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 739 B  | 
							
								
								
									
										54
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										54
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					  <meta charset="utf-8" />
 | 
				
			||||||
 | 
					  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.svg" />
 | 
				
			||||||
 | 
					  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
 | 
				
			||||||
 | 
					  <meta name="theme-color" content="#000000" />
 | 
				
			||||||
 | 
					  <meta name="keywords" content="markdown,微信公众号,微信公众号编辑器,markdown转微信公众号,微信公众号markdown" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!--
 | 
				
			||||||
 | 
					      manifest.json provides metadata used when your web app is added to the
 | 
				
			||||||
 | 
					      homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
 | 
				
			||||||
 | 
					    -->
 | 
				
			||||||
 | 
					  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
 | 
				
			||||||
 | 
					  <!--
 | 
				
			||||||
 | 
					      Notice the use of %PUBLIC_URL% in the tags above.
 | 
				
			||||||
 | 
					      It will be replaced with the URL of the `public` folder during the build.
 | 
				
			||||||
 | 
					      Only files inside the `public` folder can be referenced from the HTML.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
 | 
				
			||||||
 | 
					      work correctly both with client-side routing and a non-root public URL.
 | 
				
			||||||
 | 
					      Learn how to configure a non-root public URL by running `npm run build`.
 | 
				
			||||||
 | 
					    -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <title>Markdown2Html</title>
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					  <noscript>You need to enable JavaScript to run this app.</noscript>
 | 
				
			||||||
 | 
					  <div id="root"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <!--
 | 
				
			||||||
 | 
					      This HTML file is a template.
 | 
				
			||||||
 | 
					      If you open it directly in the browser, you will see an empty page.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You can add webfonts, meta tags, or analytics to this file.
 | 
				
			||||||
 | 
					      The build step will place the bundled scripts into the <body> tag.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      To begin the development, run `npm start` or `yarn start`.
 | 
				
			||||||
 | 
					      To create a production bundle, use `npm run build` or `yarn build`.
 | 
				
			||||||
 | 
					    -->
 | 
				
			||||||
 | 
					  <script>
 | 
				
			||||||
 | 
					    var _hmt = _hmt || [];
 | 
				
			||||||
 | 
					    (function () {
 | 
				
			||||||
 | 
					      var hm = document.createElement("script");
 | 
				
			||||||
 | 
					      hm.src = "https://hm.baidu.com/hm.js?897783afa3c6079cf0ac98fe04e7ead7";
 | 
				
			||||||
 | 
					      var s = document.getElementsByTagName("script")[0];
 | 
				
			||||||
 | 
					      s.parentNode.insertBefore(hm, s);
 | 
				
			||||||
 | 
					    })();
 | 
				
			||||||
 | 
					  </script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										20
									
								
								public/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										20
									
								
								public/manifest.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "short_name": "Markdown Nice",
 | 
				
			||||||
 | 
					  "name": "Markdown Nice",
 | 
				
			||||||
 | 
					  "icons": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "src": "https://my-wechat.mdnice.com/mdnice/mdnice%20logo_20191007150129.png",
 | 
				
			||||||
 | 
					      "sizes": "128x128",
 | 
				
			||||||
 | 
					      "type": "image/png"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "src": "https://my-wechat.mdnice.com/mdnice/mdnice%20logo_20191007150129.png",
 | 
				
			||||||
 | 
					      "sizes": "512x512",
 | 
				
			||||||
 | 
					      "type": "image/png"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "start_url": "./index.html",
 | 
				
			||||||
 | 
					  "display": "standalone",
 | 
				
			||||||
 | 
					  "theme_color": "#000000",
 | 
				
			||||||
 | 
					  "background_color": "#ffffff"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								screenshot.jpg
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								screenshot.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 127 KiB  | 
							
								
								
									
										192
									
								
								scripts/build.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										192
									
								
								scripts/build.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,192 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Do this as the first thing so that any code reading it knows the right env.
 | 
				
			||||||
 | 
					process.env.BABEL_ENV = 'production';
 | 
				
			||||||
 | 
					process.env.NODE_ENV = 'production';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Makes the script crash on unhandled rejections instead of silently
 | 
				
			||||||
 | 
					// ignoring them. In the future, promise rejections that are not handled will
 | 
				
			||||||
 | 
					// terminate the Node.js process with a non-zero exit code.
 | 
				
			||||||
 | 
					process.on('unhandledRejection', err => {
 | 
				
			||||||
 | 
					  throw err;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ensure environment variables are read.
 | 
				
			||||||
 | 
					require('../config/env');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					const chalk = require('react-dev-utils/chalk');
 | 
				
			||||||
 | 
					const fs = require('fs-extra');
 | 
				
			||||||
 | 
					const webpack = require('webpack');
 | 
				
			||||||
 | 
					const bfj = require('bfj');
 | 
				
			||||||
 | 
					const configFactory = require('../config/webpack.config');
 | 
				
			||||||
 | 
					const paths = require('../config/paths');
 | 
				
			||||||
 | 
					const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
 | 
				
			||||||
 | 
					const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
 | 
				
			||||||
 | 
					const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
 | 
				
			||||||
 | 
					const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
 | 
				
			||||||
 | 
					const printBuildError = require('react-dev-utils/printBuildError');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const measureFileSizesBeforeBuild =
 | 
				
			||||||
 | 
					  FileSizeReporter.measureFileSizesBeforeBuild;
 | 
				
			||||||
 | 
					const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
 | 
				
			||||||
 | 
					const useYarn = fs.existsSync(paths.yarnLockFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// These sizes are pretty large. We'll warn for bundles exceeding them.
 | 
				
			||||||
 | 
					const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
 | 
				
			||||||
 | 
					const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isInteractive = process.stdout.isTTY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Warn and crash if required files are missing
 | 
				
			||||||
 | 
					if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
 | 
				
			||||||
 | 
					  process.exit(1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Process CLI arguments
 | 
				
			||||||
 | 
					const argv = process.argv.slice(2);
 | 
				
			||||||
 | 
					const writeStatsJson = argv.indexOf('--stats') !== -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Generate configuration
 | 
				
			||||||
 | 
					const config = configFactory('production');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We require that you explicitly set browsers and do not fall back to
 | 
				
			||||||
 | 
					// browserslist defaults.
 | 
				
			||||||
 | 
					const { checkBrowsers } = require('react-dev-utils/browsersHelper');
 | 
				
			||||||
 | 
					checkBrowsers(paths.appPath, isInteractive)
 | 
				
			||||||
 | 
					  .then(() => {
 | 
				
			||||||
 | 
					    // First, read the current file sizes in build directory.
 | 
				
			||||||
 | 
					    // This lets us display how much they changed later.
 | 
				
			||||||
 | 
					    return measureFileSizesBeforeBuild(paths.appBuild);
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .then(previousFileSizes => {
 | 
				
			||||||
 | 
					    // Remove all content but keep the directory so that
 | 
				
			||||||
 | 
					    // if you're in it, you don't end up in Trash
 | 
				
			||||||
 | 
					    fs.emptyDirSync(paths.appBuild);
 | 
				
			||||||
 | 
					    // Merge with the public folder
 | 
				
			||||||
 | 
					    copyPublicFolder();
 | 
				
			||||||
 | 
					    // Start the webpack build
 | 
				
			||||||
 | 
					    return build(previousFileSizes);
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .then(
 | 
				
			||||||
 | 
					    ({ stats, previousFileSizes, warnings }) => {
 | 
				
			||||||
 | 
					      if (warnings.length) {
 | 
				
			||||||
 | 
					        console.log(chalk.yellow('Compiled with warnings.\n'));
 | 
				
			||||||
 | 
					        console.log(warnings.join('\n\n'));
 | 
				
			||||||
 | 
					        console.log(
 | 
				
			||||||
 | 
					          '\nSearch for the ' +
 | 
				
			||||||
 | 
					            chalk.underline(chalk.yellow('keywords')) +
 | 
				
			||||||
 | 
					            ' to learn more about each warning.'
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        console.log(
 | 
				
			||||||
 | 
					          'To ignore, add ' +
 | 
				
			||||||
 | 
					            chalk.cyan('// eslint-disable-next-line') +
 | 
				
			||||||
 | 
					            ' to the line before.\n'
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        console.log(chalk.green('Compiled successfully.\n'));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      console.log('File sizes after gzip:\n');
 | 
				
			||||||
 | 
					      printFileSizesAfterBuild(
 | 
				
			||||||
 | 
					        stats,
 | 
				
			||||||
 | 
					        previousFileSizes,
 | 
				
			||||||
 | 
					        paths.appBuild,
 | 
				
			||||||
 | 
					        WARN_AFTER_BUNDLE_GZIP_SIZE,
 | 
				
			||||||
 | 
					        WARN_AFTER_CHUNK_GZIP_SIZE
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      console.log();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const appPackage = require(paths.appPackageJson);
 | 
				
			||||||
 | 
					      const publicUrl = paths.publicUrl;
 | 
				
			||||||
 | 
					      const publicPath = config.output.publicPath;
 | 
				
			||||||
 | 
					      const buildFolder = path.relative(process.cwd(), paths.appBuild);
 | 
				
			||||||
 | 
					      printHostingInstructions(
 | 
				
			||||||
 | 
					        appPackage,
 | 
				
			||||||
 | 
					        publicUrl,
 | 
				
			||||||
 | 
					        publicPath,
 | 
				
			||||||
 | 
					        buildFolder,
 | 
				
			||||||
 | 
					        useYarn
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    err => {
 | 
				
			||||||
 | 
					      console.log(chalk.red('Failed to compile.\n'));
 | 
				
			||||||
 | 
					      printBuildError(err);
 | 
				
			||||||
 | 
					      process.exit(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  .catch(err => {
 | 
				
			||||||
 | 
					    if (err && err.message) {
 | 
				
			||||||
 | 
					      console.log(err.message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    process.exit(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create the production build and print the deployment instructions.
 | 
				
			||||||
 | 
					function build(previousFileSizes) {
 | 
				
			||||||
 | 
					  console.log('Creating an optimized production build...');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let compiler = webpack(config);
 | 
				
			||||||
 | 
					  return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					    compiler.run((err, stats) => {
 | 
				
			||||||
 | 
					      let messages;
 | 
				
			||||||
 | 
					      if (err) {
 | 
				
			||||||
 | 
					        if (!err.message) {
 | 
				
			||||||
 | 
					          return reject(err);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        messages = formatWebpackMessages({
 | 
				
			||||||
 | 
					          errors: [err.message],
 | 
				
			||||||
 | 
					          warnings: [],
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        messages = formatWebpackMessages(
 | 
				
			||||||
 | 
					          stats.toJson({ all: false, warnings: true, errors: true })
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (messages.errors.length) {
 | 
				
			||||||
 | 
					        // Only keep the first error. Others are often indicative
 | 
				
			||||||
 | 
					        // of the same problem, but confuse the reader with noise.
 | 
				
			||||||
 | 
					        if (messages.errors.length > 1) {
 | 
				
			||||||
 | 
					          messages.errors.length = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return reject(new Error(messages.errors.join('\n\n')));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        process.env.CI &&
 | 
				
			||||||
 | 
					        (typeof process.env.CI !== 'string' ||
 | 
				
			||||||
 | 
					          process.env.CI.toLowerCase() !== 'false') &&
 | 
				
			||||||
 | 
					        messages.warnings.length
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        console.log(
 | 
				
			||||||
 | 
					          chalk.yellow(
 | 
				
			||||||
 | 
					            '\nTreating warnings as errors because process.env.CI = true.\n' +
 | 
				
			||||||
 | 
					              'Most CI servers set it automatically.\n'
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        return reject(new Error(messages.warnings.join('\n\n')));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const resolveArgs = {
 | 
				
			||||||
 | 
					        stats,
 | 
				
			||||||
 | 
					        previousFileSizes,
 | 
				
			||||||
 | 
					        warnings: messages.warnings,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      if (writeStatsJson) {
 | 
				
			||||||
 | 
					        return bfj
 | 
				
			||||||
 | 
					          .write(paths.appBuild + '/bundle-stats.json', stats.toJson())
 | 
				
			||||||
 | 
					          .then(() => resolve(resolveArgs))
 | 
				
			||||||
 | 
					          .catch(error => reject(new Error(error)));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return resolve(resolveArgs);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function copyPublicFolder() {
 | 
				
			||||||
 | 
					  fs.copySync(paths.appPublic, paths.appBuild, {
 | 
				
			||||||
 | 
					    dereference: true,
 | 
				
			||||||
 | 
					    filter: file => file !== paths.appHtml,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										117
									
								
								scripts/start.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										117
									
								
								scripts/start.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Do this as the first thing so that any code reading it knows the right env.
 | 
				
			||||||
 | 
					process.env.BABEL_ENV = 'development';
 | 
				
			||||||
 | 
					process.env.NODE_ENV = 'development';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Makes the script crash on unhandled rejections instead of silently
 | 
				
			||||||
 | 
					// ignoring them. In the future, promise rejections that are not handled will
 | 
				
			||||||
 | 
					// terminate the Node.js process with a non-zero exit code.
 | 
				
			||||||
 | 
					process.on('unhandledRejection', err => {
 | 
				
			||||||
 | 
					  throw err;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ensure environment variables are read.
 | 
				
			||||||
 | 
					require('../config/env');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fs = require('fs');
 | 
				
			||||||
 | 
					const chalk = require('react-dev-utils/chalk');
 | 
				
			||||||
 | 
					const webpack = require('webpack');
 | 
				
			||||||
 | 
					const WebpackDevServer = require('webpack-dev-server');
 | 
				
			||||||
 | 
					const clearConsole = require('react-dev-utils/clearConsole');
 | 
				
			||||||
 | 
					const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
 | 
				
			||||||
 | 
					const {
 | 
				
			||||||
 | 
					  choosePort,
 | 
				
			||||||
 | 
					  createCompiler,
 | 
				
			||||||
 | 
					  prepareProxy,
 | 
				
			||||||
 | 
					  prepareUrls,
 | 
				
			||||||
 | 
					} = require('react-dev-utils/WebpackDevServerUtils');
 | 
				
			||||||
 | 
					const openBrowser = require('react-dev-utils/openBrowser');
 | 
				
			||||||
 | 
					const paths = require('../config/paths');
 | 
				
			||||||
 | 
					const configFactory = require('../config/webpack.config');
 | 
				
			||||||
 | 
					const createDevServerConfig = require('../config/webpackDevServer.config');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useYarn = fs.existsSync(paths.yarnLockFile);
 | 
				
			||||||
 | 
					const isInteractive = process.stdout.isTTY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Warn and crash if required files are missing
 | 
				
			||||||
 | 
					if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
 | 
				
			||||||
 | 
					  process.exit(1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Tools like Cloud9 rely on this.
 | 
				
			||||||
 | 
					const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
 | 
				
			||||||
 | 
					const HOST = process.env.HOST || '0.0.0.0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (process.env.HOST) {
 | 
				
			||||||
 | 
					  console.log(
 | 
				
			||||||
 | 
					    chalk.cyan(
 | 
				
			||||||
 | 
					      `Attempting to bind to HOST environment variable: ${chalk.yellow(
 | 
				
			||||||
 | 
					        chalk.bold(process.env.HOST)
 | 
				
			||||||
 | 
					      )}`
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  console.log(
 | 
				
			||||||
 | 
					    `If this was unintentional, check that you haven't mistakenly set it in your shell.`
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  console.log(
 | 
				
			||||||
 | 
					    `Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  console.log();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We require that you explictly set browsers and do not fall back to
 | 
				
			||||||
 | 
					// browserslist defaults.
 | 
				
			||||||
 | 
					const { checkBrowsers } = require('react-dev-utils/browsersHelper');
 | 
				
			||||||
 | 
					checkBrowsers(paths.appPath, isInteractive)
 | 
				
			||||||
 | 
					  .then(() => {
 | 
				
			||||||
 | 
					    // We attempt to use the default port but if it is busy, we offer the user to
 | 
				
			||||||
 | 
					    // run on a different port. `choosePort()` Promise resolves to the next free port.
 | 
				
			||||||
 | 
					    return choosePort(HOST, DEFAULT_PORT);
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .then(port => {
 | 
				
			||||||
 | 
					    if (port == null) {
 | 
				
			||||||
 | 
					      // We have not found a port.
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const config = configFactory('development');
 | 
				
			||||||
 | 
					    const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
 | 
				
			||||||
 | 
					    const appName = require(paths.appPackageJson).name;
 | 
				
			||||||
 | 
					    const urls = prepareUrls(protocol, HOST, port);
 | 
				
			||||||
 | 
					    // Create a webpack compiler that is configured with custom messages.
 | 
				
			||||||
 | 
					    const compiler = createCompiler(webpack, config, appName, urls, useYarn);
 | 
				
			||||||
 | 
					    // Load proxy config
 | 
				
			||||||
 | 
					    const proxySetting = require(paths.appPackageJson).proxy;
 | 
				
			||||||
 | 
					    const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
 | 
				
			||||||
 | 
					    // Serve webpack assets generated by the compiler over a web server.
 | 
				
			||||||
 | 
					    const serverConfig = createDevServerConfig(
 | 
				
			||||||
 | 
					      proxyConfig,
 | 
				
			||||||
 | 
					      urls.lanUrlForConfig
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    const devServer = new WebpackDevServer(compiler, serverConfig);
 | 
				
			||||||
 | 
					    // Launch WebpackDevServer.
 | 
				
			||||||
 | 
					    devServer.listen(port, HOST, err => {
 | 
				
			||||||
 | 
					      if (err) {
 | 
				
			||||||
 | 
					        return console.log(err);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (isInteractive) {
 | 
				
			||||||
 | 
					        clearConsole();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      console.log(chalk.cyan('Starting the development server...\n'));
 | 
				
			||||||
 | 
					      openBrowser(urls.localUrlForBrowser);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ['SIGINT', 'SIGTERM'].forEach(function(sig) {
 | 
				
			||||||
 | 
					      process.on(sig, function() {
 | 
				
			||||||
 | 
					        devServer.close();
 | 
				
			||||||
 | 
					        process.exit();
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .catch(err => {
 | 
				
			||||||
 | 
					    if (err && err.message) {
 | 
				
			||||||
 | 
					      console.log(err.message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    process.exit(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
							
								
								
									
										60
									
								
								scripts/test.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										60
									
								
								scripts/test.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Do this as the first thing so that any code reading it knows the right env.
 | 
				
			||||||
 | 
					process.env.BABEL_ENV = 'test';
 | 
				
			||||||
 | 
					process.env.NODE_ENV = 'test';
 | 
				
			||||||
 | 
					process.env.PUBLIC_URL = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Makes the script crash on unhandled rejections instead of silently
 | 
				
			||||||
 | 
					// ignoring them. In the future, promise rejections that are not handled will
 | 
				
			||||||
 | 
					// terminate the Node.js process with a non-zero exit code.
 | 
				
			||||||
 | 
					process.on('unhandledRejection', err => {
 | 
				
			||||||
 | 
					  throw err;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ensure environment variables are read.
 | 
				
			||||||
 | 
					require('../config/env');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const jest = require('jest');
 | 
				
			||||||
 | 
					const execSync = require('child_process').execSync;
 | 
				
			||||||
 | 
					let argv = process.argv.slice(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function isInGitRepository() {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function isInMercurialRepository() {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    execSync('hg --cwd . root', { stdio: 'ignore' });
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Watch unless on CI, in coverage mode, explicitly adding `--no-watch`,
 | 
				
			||||||
 | 
					// or explicitly running all tests
 | 
				
			||||||
 | 
					if (
 | 
				
			||||||
 | 
					  !process.env.CI &&
 | 
				
			||||||
 | 
					  argv.indexOf('--coverage') === -1 &&
 | 
				
			||||||
 | 
					  argv.indexOf('--no-watch') === -1 &&
 | 
				
			||||||
 | 
					  argv.indexOf('--watchAll') === -1
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  // https://github.com/facebook/create-react-app/issues/5210
 | 
				
			||||||
 | 
					  const hasSourceControl = isInGitRepository() || isInMercurialRepository();
 | 
				
			||||||
 | 
					  argv.push(hasSourceControl ? '--watch' : '--watchAll');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Jest doesn't have this option so we'll remove it
 | 
				
			||||||
 | 
					if (argv.indexOf('--no-watch') !== -1) {
 | 
				
			||||||
 | 
					  argv = argv.filter(arg => arg !== '--no-watch');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.run(argv);
 | 
				
			||||||
							
								
								
									
										210
									
								
								src/App.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										210
									
								
								src/App.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,210 @@
 | 
				
			|||||||
 | 
					.ant-btn-primary {
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  background-color: #000;
 | 
				
			||||||
 | 
					  border-color: #000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-btn-primary:hover,
 | 
				
			||||||
 | 
					.ant-btn-primary:focus {
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  background-color: #000;
 | 
				
			||||||
 | 
					  border-color: #000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-btn:hover, .ant-btn:focus {
 | 
				
			||||||
 | 
					  color: #000;
 | 
				
			||||||
 | 
					  background-color: #fff;
 | 
				
			||||||
 | 
					  border-color: #000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-input:hover, .ant-input:focus {
 | 
				
			||||||
 | 
					  border-color: #000;
 | 
				
			||||||
 | 
					  border-right-width: 1px !important;
 | 
				
			||||||
 | 
					  -webkit-box-shadow: 0 0 0 2px rgb(82 82 83 / 20%);
 | 
				
			||||||
 | 
					  box-shadow: 0 0 0 2px rgb(82 82 83 / 20%);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-select-selection:hover {
 | 
				
			||||||
 | 
					  border-color: #000;
 | 
				
			||||||
 | 
					  border-right-width: 1px !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-select-focused .ant-select-selection, .ant-select-selection:focus, .ant-select-selection:active,
 | 
				
			||||||
 | 
					.ant-input-number:focus, .ant-input-number:active , .ant-input-number:hover {
 | 
				
			||||||
 | 
					  border-color: #000;
 | 
				
			||||||
 | 
					  -webkit-box-shadow: 0 0 0 2px rgb(82 82 83 / 20%);
 | 
				
			||||||
 | 
					  box-shadow: 0 0 0 2px rgb(82 82 83 / 20%);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
 | 
				
			||||||
 | 
					  background-color: #f1f1f1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-select-dropdown-menu {
 | 
				
			||||||
 | 
					  margin: 4px;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-select-dropdown-menu-item {
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  margin: 2px 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-menu-item-selected {
 | 
				
			||||||
 | 
					  color: #000;
 | 
				
			||||||
 | 
					  /*font-weight: bolder;*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
 | 
				
			||||||
 | 
					  background-color: #f6f6f6;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-menu-item {
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  margin-right: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-menu-inline, .ant-menu-vertical, .ant-menu-vertical-left {
 | 
				
			||||||
 | 
					  margin-right: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ant-menu-item:hover, .ant-menu-item-active, .ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open, .ant-menu-submenu-active, .ant-menu-submenu-title:hover {
 | 
				
			||||||
 | 
					  color: #000;
 | 
				
			||||||
 | 
					  font-weight: bolder;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-app {
 | 
				
			||||||
 | 
					  height: 100vh;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  font-family: "PingFang SC", BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serifhtml, body;
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-text-container {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  height: calc(100vh - 50px);
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-text-container-immersive {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  height: 100vh;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-editing-immersive {
 | 
				
			||||||
 | 
					  padding: 0px;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-editing-immersive .CodeMirror-lines {
 | 
				
			||||||
 | 
					  padding: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media screen and (min-width: 768px) {
 | 
				
			||||||
 | 
					  .nice-md-editing-immersive .CodeMirror-lines {
 | 
				
			||||||
 | 
					    padding: 20px 10%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media screen and (min-width: 1024px) {
 | 
				
			||||||
 | 
					  .nice-md-editing-immersive .CodeMirror-lines {
 | 
				
			||||||
 | 
					    padding: 20px 15%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 编辑器最多会被分成三份width:33.3%,当两份时根据flex-grow:1伸展 */
 | 
				
			||||||
 | 
					.nice-md-editing,
 | 
				
			||||||
 | 
					.nice-style-editing {
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  width: 33.3%;
 | 
				
			||||||
 | 
					  height: 88%;
 | 
				
			||||||
 | 
					  flex-grow: 1;
 | 
				
			||||||
 | 
					  word-wrap: break-word;
 | 
				
			||||||
 | 
					  z-index: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-marked-text {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  width: 33.3%;
 | 
				
			||||||
 | 
					  flex-grow: 1;
 | 
				
			||||||
 | 
					  padding: 20px;
 | 
				
			||||||
 | 
					  margin-bottom: 70px;
 | 
				
			||||||
 | 
					  word-wrap: break-word;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-marked-text-pc {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-wx-box {
 | 
				
			||||||
 | 
					  overflow-y: auto;
 | 
				
			||||||
 | 
					  padding: 25px 20px;
 | 
				
			||||||
 | 
					  height: 98%;
 | 
				
			||||||
 | 
					  width: 375px;
 | 
				
			||||||
 | 
					  box-shadow: 0 0 60px rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-wx-box-pc {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  padding: 20px 35px 20px 20px;
 | 
				
			||||||
 | 
					  box-shadow: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-style-editing-hide {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-editing-hide {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-marked-text-hide {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					::-webkit-scrollbar {
 | 
				
			||||||
 | 
					  width: 6px;
 | 
				
			||||||
 | 
					  height: 6px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					::-webkit-scrollbar-track {
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  background: rgba(0, 0, 0, 0.06);
 | 
				
			||||||
 | 
					  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.08);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					::-webkit-scrollbar-thumb {
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  background: rgba(0, 0, 0, 0.12);
 | 
				
			||||||
 | 
					  box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media print {
 | 
				
			||||||
 | 
					  .nice-md-editing {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nice-navbar {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nice-sidebar {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nice-wx-box {
 | 
				
			||||||
 | 
					    overflow: visible;
 | 
				
			||||||
 | 
					    box-shadow: none;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nice-style-editing {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  #nice-rich-text {
 | 
				
			||||||
 | 
					    padding: 0 !important;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .nice-footer-container {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										410
									
								
								src/App.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										410
									
								
								src/App.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,410 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import CodeMirror from "@uiw/react-codemirror";
 | 
				
			||||||
 | 
					import "codemirror/addon/search/searchcursor";
 | 
				
			||||||
 | 
					import "codemirror/keymap/sublime";
 | 
				
			||||||
 | 
					import "antd/dist/antd.css";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import classnames from "classnames";
 | 
				
			||||||
 | 
					import throttle from "lodash.throttle";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Dialog from "./layout/Dialog";
 | 
				
			||||||
 | 
					import Navbar from "./layout/Navbar";
 | 
				
			||||||
 | 
					import Toobar from "./layout/Toolbar";
 | 
				
			||||||
 | 
					import Footer from "./layout/Footer";
 | 
				
			||||||
 | 
					import Sidebar from "./layout/Sidebar";
 | 
				
			||||||
 | 
					import StyleEditor from "./layout/StyleEditor";
 | 
				
			||||||
 | 
					import EditorMenu from "./layout/EditorMenu";
 | 
				
			||||||
 | 
					import SearchBox from "./component/SearchBox";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./App.css";
 | 
				
			||||||
 | 
					import "./utils/mdMirror.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  LAYOUT_ID,
 | 
				
			||||||
 | 
					  BOX_ID,
 | 
				
			||||||
 | 
					  IMAGE_HOSTING_NAMES,
 | 
				
			||||||
 | 
					  IMAGE_HOSTING_TYPE,
 | 
				
			||||||
 | 
					  MJX_DATA_FORMULA,
 | 
				
			||||||
 | 
					  MJX_DATA_FORMULA_TYPE,
 | 
				
			||||||
 | 
					} from "./utils/constant";
 | 
				
			||||||
 | 
					import {markdownParser, markdownParserWechat, updateMathjax} from "./utils/helper";
 | 
				
			||||||
 | 
					import pluginCenter from "./utils/pluginCenter";
 | 
				
			||||||
 | 
					import appContext from "./utils/appContext";
 | 
				
			||||||
 | 
					import {uploadAdaptor} from "./utils/imageHosting";
 | 
				
			||||||
 | 
					import bindHotkeys, {betterTab, rightClick} from "./utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@inject("footer")
 | 
				
			||||||
 | 
					@inject("view")
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class App extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.scale = 1;
 | 
				
			||||||
 | 
					    this.handleUpdateMathjax = throttle(updateMathjax, 1500);
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      focus: false,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentDidMount() {
 | 
				
			||||||
 | 
					    document.addEventListener("fullscreenchange", this.solveScreenChange);
 | 
				
			||||||
 | 
					    document.addEventListener("webkitfullscreenchange", this.solveScreenChange);
 | 
				
			||||||
 | 
					    document.addEventListener("mozfullscreenchange", this.solveScreenChange);
 | 
				
			||||||
 | 
					    document.addEventListener("MSFullscreenChange", this.solveScreenChange);
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      window.MathJax = {
 | 
				
			||||||
 | 
					        tex: {
 | 
				
			||||||
 | 
					          inlineMath: [["$", "$"]],
 | 
				
			||||||
 | 
					          displayMath: [["$$", "$$"]],
 | 
				
			||||||
 | 
					          tags: "ams",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        svg: {
 | 
				
			||||||
 | 
					          fontCache: "none",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        options: {
 | 
				
			||||||
 | 
					          renderActions: {
 | 
				
			||||||
 | 
					            addMenu: [0, "", ""],
 | 
				
			||||||
 | 
					            addContainer: [
 | 
				
			||||||
 | 
					              190,
 | 
				
			||||||
 | 
					              (doc) => {
 | 
				
			||||||
 | 
					                for (const math of doc.math) {
 | 
				
			||||||
 | 
					                  this.addContainer(math, doc);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              this.addContainer,
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      // eslint-disable-next-line
 | 
				
			||||||
 | 
					      require("mathjax/es5/tex-svg-full");
 | 
				
			||||||
 | 
					      pluginCenter.mathjax = true;
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      console.log(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.setEditorContent();
 | 
				
			||||||
 | 
					    this.setCustomImageHosting();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentDidUpdate() {
 | 
				
			||||||
 | 
					    if (pluginCenter.mathjax) {
 | 
				
			||||||
 | 
					      this.handleUpdateMathjax();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentWillUnmount() {
 | 
				
			||||||
 | 
					    document.removeEventListener("fullscreenchange", this.solveScreenChange);
 | 
				
			||||||
 | 
					    document.removeEventListener("webkitfullscreenchange", this.solveScreenChange);
 | 
				
			||||||
 | 
					    document.removeEventListener("mozfullscreenchange", this.solveScreenChange);
 | 
				
			||||||
 | 
					    document.removeEventListener("MSFullscreenChange", this.solveScreenChange);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setCustomImageHosting = () => {
 | 
				
			||||||
 | 
					    if (this.props.useImageHosting === undefined) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const {url, name, isSmmsOpen, isQiniuyunOpen, isAliyunOpen, isGiteeOpen, isGitHubOpen} = this.props.useImageHosting;
 | 
				
			||||||
 | 
					    if (name) {
 | 
				
			||||||
 | 
					      this.props.imageHosting.setHostingUrl(url);
 | 
				
			||||||
 | 
					      this.props.imageHosting.setHostingName(name);
 | 
				
			||||||
 | 
					      this.props.imageHosting.addImageHosting(name);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (isSmmsOpen) {
 | 
				
			||||||
 | 
					      this.props.imageHosting.addImageHosting(IMAGE_HOSTING_NAMES.smms);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (isAliyunOpen) {
 | 
				
			||||||
 | 
					      this.props.imageHosting.addImageHosting(IMAGE_HOSTING_NAMES.aliyun);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (isQiniuyunOpen) {
 | 
				
			||||||
 | 
					      this.props.imageHosting.addImageHosting(IMAGE_HOSTING_NAMES.qiniuyun);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (isGiteeOpen) {
 | 
				
			||||||
 | 
					      this.props.imageHosting.addImageHosting(IMAGE_HOSTING_NAMES.gitee);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (isGitHubOpen) {
 | 
				
			||||||
 | 
					      this.props.imageHosting.addImageHosting(IMAGE_HOSTING_NAMES.github);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 第一次进入没有默认图床时
 | 
				
			||||||
 | 
					    if (window.localStorage.getItem(IMAGE_HOSTING_TYPE) === null) {
 | 
				
			||||||
 | 
					      let type;
 | 
				
			||||||
 | 
					      if (name) {
 | 
				
			||||||
 | 
					        type = name;
 | 
				
			||||||
 | 
					      } else if (isSmmsOpen) {
 | 
				
			||||||
 | 
					        type = IMAGE_HOSTING_NAMES.smms;
 | 
				
			||||||
 | 
					      } else if (isAliyunOpen) {
 | 
				
			||||||
 | 
					        type = IMAGE_HOSTING_NAMES.aliyun;
 | 
				
			||||||
 | 
					      } else if (isQiniuyunOpen) {
 | 
				
			||||||
 | 
					        type = IMAGE_HOSTING_NAMES.qiniuyun;
 | 
				
			||||||
 | 
					      } else if (isGiteeOpen) {
 | 
				
			||||||
 | 
					        type = IMAGE_HOSTING_NAMES.isGitee;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.props.imageHosting.setType(type);
 | 
				
			||||||
 | 
					      window.localStorage.setItem(IMAGE_HOSTING_TYPE, type);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setEditorContent = () => {
 | 
				
			||||||
 | 
					    const {defaultText} = this.props;
 | 
				
			||||||
 | 
					    if (defaultText) {
 | 
				
			||||||
 | 
					      this.props.content.setContent(defaultText);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setCurrentIndex(index) {
 | 
				
			||||||
 | 
					    this.index = index;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  solveScreenChange = () => {
 | 
				
			||||||
 | 
					    const {isImmersiveEditing} = this.props.view;
 | 
				
			||||||
 | 
					    this.props.view.setImmersiveEditing(!isImmersiveEditing);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getInstance = (instance) => {
 | 
				
			||||||
 | 
					    instance.editor.on("inputRead", function(cm, event) {
 | 
				
			||||||
 | 
					      if (event.origin === "paste") {
 | 
				
			||||||
 | 
					        var text = event.text[0]; // pasted string
 | 
				
			||||||
 | 
					        var new_text = ""; // any operations here
 | 
				
			||||||
 | 
					        cm.refresh();
 | 
				
			||||||
 | 
					        const {length} = cm.getSelections();
 | 
				
			||||||
 | 
					        // my first idea was
 | 
				
			||||||
 | 
					        // note: for multiline strings may need more complex calculations
 | 
				
			||||||
 | 
					        cm.replaceRange(new_text, event.from, {line: event.from.line, ch: event.from.ch + text.length});
 | 
				
			||||||
 | 
					        // first solution did'nt work (before i guess to call refresh) so i tried that way, works too
 | 
				
			||||||
 | 
					        if (length === 1) {
 | 
				
			||||||
 | 
					          cm.execCommand("undo");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // cm.setCursor(event.from);
 | 
				
			||||||
 | 
					        cm.replaceSelection(new_text);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    if (instance) {
 | 
				
			||||||
 | 
					      this.props.content.setMarkdownEditor(instance.editor);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleScroll = () => {
 | 
				
			||||||
 | 
					    if (this.props.navbar.isSyncScroll) {
 | 
				
			||||||
 | 
					      const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					      const cmData = markdownEditor.getScrollInfo();
 | 
				
			||||||
 | 
					      const editorToTop = cmData.top;
 | 
				
			||||||
 | 
					      const editorScrollHeight = cmData.height - cmData.clientHeight;
 | 
				
			||||||
 | 
					      this.scale = (this.previewWrap.offsetHeight - this.previewContainer.offsetHeight + 55) / editorScrollHeight;
 | 
				
			||||||
 | 
					      if (this.index === 1) {
 | 
				
			||||||
 | 
					        this.previewContainer.scrollTop = editorToTop * this.scale;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.editorTop = this.previewContainer.scrollTop / this.scale;
 | 
				
			||||||
 | 
					        markdownEditor.scrollTo(null, this.editorTop);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleChange = (editor) => {
 | 
				
			||||||
 | 
					    if (this.state.focus) {
 | 
				
			||||||
 | 
					      const content = editor.getValue();
 | 
				
			||||||
 | 
					      this.props.content.setContent(content);
 | 
				
			||||||
 | 
					      this.props.onTextChange && this.props.onTextChange(content);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleFocus = (editor) => {
 | 
				
			||||||
 | 
					    this.setState({
 | 
				
			||||||
 | 
					      focus: true,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    this.props.onTextFocus && this.props.onTextFocus(editor.getValue());
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleBlur = (editor) => {
 | 
				
			||||||
 | 
					    this.setState({
 | 
				
			||||||
 | 
					      focus: false,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    this.props.onTextBlur && this.props.onTextBlur(editor.getValue());
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getStyleInstance = (instance) => {
 | 
				
			||||||
 | 
					    if (instance) {
 | 
				
			||||||
 | 
					      this.styleEditor = instance.editor;
 | 
				
			||||||
 | 
					      this.styleEditor.on("keyup", (cm, e) => {
 | 
				
			||||||
 | 
					        if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
 | 
				
			||||||
 | 
					          cm.showHint(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleDrop = (instance, e) => {
 | 
				
			||||||
 | 
					    // e.preventDefault();
 | 
				
			||||||
 | 
					    // console.log(e.dataTransfer.files[0]);
 | 
				
			||||||
 | 
					    if (!(e.dataTransfer && e.dataTransfer.files)) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (let i = 0; i < e.dataTransfer.files.length; i++) {
 | 
				
			||||||
 | 
					      // console.log(e.dataTransfer.files[i]);
 | 
				
			||||||
 | 
					      uploadAdaptor({file: e.dataTransfer.files[i], content: this.props.content});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handlePaste = (instance, e) => {
 | 
				
			||||||
 | 
					    const cbData = e.clipboardData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const insertPasteContent = (cm, content) => {
 | 
				
			||||||
 | 
					      const {length} = cm.getSelections();
 | 
				
			||||||
 | 
					      cm.replaceSelections(Array(length).fill(content));
 | 
				
			||||||
 | 
					      this.setState(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          focus: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        () => {
 | 
				
			||||||
 | 
					          this.handleChange(cm);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (e.clipboardData && e.clipboardData.files) {
 | 
				
			||||||
 | 
					      for (let i = 0; i < e.clipboardData.files.length; i++) {
 | 
				
			||||||
 | 
					        uploadAdaptor({file: e.clipboardData.files[i], content: this.props.content});
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (cbData) {
 | 
				
			||||||
 | 
					      const html = cbData.getData("text/html");
 | 
				
			||||||
 | 
					      const text = cbData.getData("TEXT");
 | 
				
			||||||
 | 
					      insertPasteContent(instance, text);
 | 
				
			||||||
 | 
					      console.log(html);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (html) {
 | 
				
			||||||
 | 
					        console.log("htmsdkskdkskdk");
 | 
				
			||||||
 | 
					        this.props.footer.setPasteHtmlChecked(true);
 | 
				
			||||||
 | 
					        this.props.footer.setPasteHtml(html);
 | 
				
			||||||
 | 
					        this.props.footer.setPasteText(text);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.props.footer.setPasteHtmlChecked(false);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addContainer(math, doc) {
 | 
				
			||||||
 | 
					    const tag = "span";
 | 
				
			||||||
 | 
					    const spanClass = math.display ? "span-block-equation" : "span-inline-equation";
 | 
				
			||||||
 | 
					    const cls = math.display ? "block-equation" : "inline-equation";
 | 
				
			||||||
 | 
					    math.typesetRoot.className = cls;
 | 
				
			||||||
 | 
					    math.typesetRoot.setAttribute(MJX_DATA_FORMULA, math.math);
 | 
				
			||||||
 | 
					    math.typesetRoot.setAttribute(MJX_DATA_FORMULA_TYPE, cls);
 | 
				
			||||||
 | 
					    math.typesetRoot = doc.adaptor.node(tag, {class: spanClass, style: "cursor:pointer"}, [math.typesetRoot]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {codeNum, previewType} = this.props.navbar;
 | 
				
			||||||
 | 
					    const {isEditAreaOpen, isPreviewAreaOpen, isStyleEditorOpen, isImmersiveEditing} = this.props.view;
 | 
				
			||||||
 | 
					    const {isSearchOpen} = this.props.dialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const parseHtml =
 | 
				
			||||||
 | 
					      codeNum === 0
 | 
				
			||||||
 | 
					        ? markdownParserWechat.render(this.props.content.content)
 | 
				
			||||||
 | 
					        : markdownParser.render(this.props.content.content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const mdEditingClass = classnames({
 | 
				
			||||||
 | 
					      "nice-md-editing": !isImmersiveEditing,
 | 
				
			||||||
 | 
					      "nice-md-editing-immersive": isImmersiveEditing,
 | 
				
			||||||
 | 
					      "nice-md-editing-hide": !isEditAreaOpen,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const styleEditingClass = classnames({
 | 
				
			||||||
 | 
					      "nice-style-editing": true,
 | 
				
			||||||
 | 
					      "nice-style-editing-hide": isImmersiveEditing,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const richTextClass = classnames({
 | 
				
			||||||
 | 
					      "nice-marked-text": true,
 | 
				
			||||||
 | 
					      "nice-marked-text-pc": previewType === "pc",
 | 
				
			||||||
 | 
					      "nice-marked-text-hide": isImmersiveEditing || !isPreviewAreaOpen,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const richTextBoxClass = classnames({
 | 
				
			||||||
 | 
					      "nice-wx-box": true,
 | 
				
			||||||
 | 
					      "nice-wx-box-pc": previewType === "pc",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const textContainerClass = classnames({
 | 
				
			||||||
 | 
					      "nice-text-container": !isImmersiveEditing,
 | 
				
			||||||
 | 
					      "nice-text-container-immersive": isImmersiveEditing,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <appContext.Consumer>
 | 
				
			||||||
 | 
					        {({defaultTitle, onStyleChange, onStyleBlur, onStyleFocus, token}) => (
 | 
				
			||||||
 | 
					          <div className="nice-app">
 | 
				
			||||||
 | 
					            <Navbar title={defaultTitle} token={token} />
 | 
				
			||||||
 | 
					            <Toobar token={token} />
 | 
				
			||||||
 | 
					            <div className={textContainerClass}>
 | 
				
			||||||
 | 
					              <div id="nice-md-editor" className={mdEditingClass} onMouseOver={(e) => this.setCurrentIndex(1, e)}>
 | 
				
			||||||
 | 
					                {isSearchOpen && <SearchBox />}
 | 
				
			||||||
 | 
					                <CodeMirror
 | 
				
			||||||
 | 
					                  value={this.props.content.content}
 | 
				
			||||||
 | 
					                  options={{
 | 
				
			||||||
 | 
					                    theme: "md-mirror",
 | 
				
			||||||
 | 
					                    keyMap: "sublime",
 | 
				
			||||||
 | 
					                    mode: "markdown",
 | 
				
			||||||
 | 
					                    lineWrapping: true,
 | 
				
			||||||
 | 
					                    lineNumbers: false,
 | 
				
			||||||
 | 
					                    extraKeys: {
 | 
				
			||||||
 | 
					                      ...bindHotkeys(this.props.content, this.props.dialog),
 | 
				
			||||||
 | 
					                      Tab: betterTab,
 | 
				
			||||||
 | 
					                      RightClick: rightClick,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  onChange={this.handleChange}
 | 
				
			||||||
 | 
					                  onScroll={this.handleScroll}
 | 
				
			||||||
 | 
					                  onFocus={this.handleFocus}
 | 
				
			||||||
 | 
					                  onBlur={this.handleBlur}
 | 
				
			||||||
 | 
					                  onDrop={this.handleDrop}
 | 
				
			||||||
 | 
					                  onPaste={this.handlePaste}
 | 
				
			||||||
 | 
					                  ref={this.getInstance}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <div id="nice-rich-text" className={richTextClass} onMouseOver={(e) => this.setCurrentIndex(2, e)}>
 | 
				
			||||||
 | 
					                <Sidebar />
 | 
				
			||||||
 | 
					                <div
 | 
				
			||||||
 | 
					                  id={BOX_ID}
 | 
				
			||||||
 | 
					                  className={richTextBoxClass}
 | 
				
			||||||
 | 
					                  onScroll={this.handleScroll}
 | 
				
			||||||
 | 
					                  ref={(node) => {
 | 
				
			||||||
 | 
					                    this.previewContainer = node;
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <section
 | 
				
			||||||
 | 
					                    id={LAYOUT_ID}
 | 
				
			||||||
 | 
					                    data-tool="markdown2wechat编辑器"
 | 
				
			||||||
 | 
					                    data-website="https://aizhuanqian.com"
 | 
				
			||||||
 | 
					                    dangerouslySetInnerHTML={{
 | 
				
			||||||
 | 
					                      __html: parseHtml,
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                    ref={(node) => {
 | 
				
			||||||
 | 
					                      this.previewWrap = node;
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              {isStyleEditorOpen && (
 | 
				
			||||||
 | 
					                <div id="nice-style-editor" className={styleEditingClass}>
 | 
				
			||||||
 | 
					                  <StyleEditor onStyleChange={onStyleChange} onStyleBlur={onStyleBlur} onStyleFocus={onStyleFocus} />
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              <Dialog />
 | 
				
			||||||
 | 
					              <EditorMenu />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <Footer />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </appContext.Consumer>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default App;
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/App.test.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										9
									
								
								src/App.test.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import ReactDOM from 'react-dom';
 | 
				
			||||||
 | 
					import App from './App';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					it('renders without crashing', () => {
 | 
				
			||||||
 | 
					  const div = document.createElement('div');
 | 
				
			||||||
 | 
					  ReactDOM.render(<App />, div);
 | 
				
			||||||
 | 
					  ReactDOM.unmountComponentAtNode(div);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										155
									
								
								src/Lib.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										155
									
								
								src/Lib.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,155 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import PropTypes from "prop-types";
 | 
				
			||||||
 | 
					import {Result} from "antd";
 | 
				
			||||||
 | 
					import {Provider} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./index.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import App from "./App";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import content from "./store/content";
 | 
				
			||||||
 | 
					import userInfo from "./store/userInfo";
 | 
				
			||||||
 | 
					import navbar from "./store/navbar";
 | 
				
			||||||
 | 
					import footer from "./store/footer";
 | 
				
			||||||
 | 
					import dialog from "./store/dialog";
 | 
				
			||||||
 | 
					import imageHosting from "./store/imageHosting";
 | 
				
			||||||
 | 
					import view from "./store/view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {isPC} from "./utils/helper";
 | 
				
			||||||
 | 
					import appContext from "./utils/appContext";
 | 
				
			||||||
 | 
					import SvgIcon from "./icon";
 | 
				
			||||||
 | 
					import {solveWeChatMath, solveZhihuMath, solveHtml} from "./utils/converter";
 | 
				
			||||||
 | 
					import {LAYOUT_ID} from "./utils/constant";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Lib extends Component {
 | 
				
			||||||
 | 
					  getWeChatHtml() {
 | 
				
			||||||
 | 
					    const layout = document.getElementById(LAYOUT_ID); // 保护现场
 | 
				
			||||||
 | 
					    const html = layout.innerHTML;
 | 
				
			||||||
 | 
					    solveWeChatMath();
 | 
				
			||||||
 | 
					    const res = solveHtml();
 | 
				
			||||||
 | 
					    layout.innerHTML = html; // 恢复现场
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getZhihuHtml() {
 | 
				
			||||||
 | 
					    const layout = document.getElementById(LAYOUT_ID); // 保护现场
 | 
				
			||||||
 | 
					    const html = layout.innerHTML;
 | 
				
			||||||
 | 
					    solveZhihuMath();
 | 
				
			||||||
 | 
					    const res = solveHtml();
 | 
				
			||||||
 | 
					    layout.innerHTML = html; // 恢复现场
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					      defaultTitle,
 | 
				
			||||||
 | 
					      defaultText,
 | 
				
			||||||
 | 
					      onTextChange,
 | 
				
			||||||
 | 
					      onTextBlur,
 | 
				
			||||||
 | 
					      onTextFocus,
 | 
				
			||||||
 | 
					      onStyleChange,
 | 
				
			||||||
 | 
					      onStyleBlur,
 | 
				
			||||||
 | 
					      onStyleFocus,
 | 
				
			||||||
 | 
					      token,
 | 
				
			||||||
 | 
					      useImageHosting,
 | 
				
			||||||
 | 
					    } = this.props;
 | 
				
			||||||
 | 
					    const appCtx = {
 | 
				
			||||||
 | 
					      defaultTitle,
 | 
				
			||||||
 | 
					      defaultText,
 | 
				
			||||||
 | 
					      onTextChange,
 | 
				
			||||||
 | 
					      onTextBlur,
 | 
				
			||||||
 | 
					      onTextFocus,
 | 
				
			||||||
 | 
					      onStyleChange,
 | 
				
			||||||
 | 
					      onStyleBlur,
 | 
				
			||||||
 | 
					      onStyleFocus,
 | 
				
			||||||
 | 
					      token,
 | 
				
			||||||
 | 
					      useImageHosting,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Provider
 | 
				
			||||||
 | 
					        content={content}
 | 
				
			||||||
 | 
					        userInfo={userInfo}
 | 
				
			||||||
 | 
					        navbar={navbar}
 | 
				
			||||||
 | 
					        footer={footer}
 | 
				
			||||||
 | 
					        dialog={dialog}
 | 
				
			||||||
 | 
					        imageHosting={imageHosting}
 | 
				
			||||||
 | 
					        view={view}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {isPC() ? (
 | 
				
			||||||
 | 
					          <appContext.Provider value={appCtx}>
 | 
				
			||||||
 | 
					            <App
 | 
				
			||||||
 | 
					              defaultText={defaultText}
 | 
				
			||||||
 | 
					              onTextChange={onTextChange}
 | 
				
			||||||
 | 
					              onTextBlur={onTextBlur}
 | 
				
			||||||
 | 
					              onTextFocus={onTextFocus}
 | 
				
			||||||
 | 
					              onStyleChange={onStyleChange}
 | 
				
			||||||
 | 
					              onStyleBlur={onStyleBlur}
 | 
				
			||||||
 | 
					              onStyleFocus={onStyleFocus}
 | 
				
			||||||
 | 
					              useImageHosting={useImageHosting}
 | 
				
			||||||
 | 
					              token={token}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </appContext.Provider>
 | 
				
			||||||
 | 
					        ) : (
 | 
				
			||||||
 | 
					          <Result
 | 
				
			||||||
 | 
					            icon={<SvgIcon name="smile" style={style.svgIcon} />}
 | 
				
			||||||
 | 
					            title="请使用 PC 端打开排版工具"
 | 
				
			||||||
 | 
					            subTitle="更多 Markdown Nice 信息,请扫码关注公众号「编程如画」"
 | 
				
			||||||
 | 
					            extra={<img alt="" style={{width: "100%"}} src="https://my-wechat.mdnice.com/wechat.jpg" />}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </Provider>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const style = {
 | 
				
			||||||
 | 
					  svgIcon: {
 | 
				
			||||||
 | 
					    width: "72px",
 | 
				
			||||||
 | 
					    height: "72px",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Lib.defaultProps = {
 | 
				
			||||||
 | 
					  defaultTitle: "",
 | 
				
			||||||
 | 
					  defaultText: "",
 | 
				
			||||||
 | 
					  onTextChange: () => {},
 | 
				
			||||||
 | 
					  onTextBlur: () => {},
 | 
				
			||||||
 | 
					  onTextFocus: () => {},
 | 
				
			||||||
 | 
					  onStyleChange: () => {},
 | 
				
			||||||
 | 
					  onStyleBlur: () => {},
 | 
				
			||||||
 | 
					  onStyleFocus: () => {},
 | 
				
			||||||
 | 
					  token: "",
 | 
				
			||||||
 | 
					  // eslint-disable-next-line react/default-props-match-prop-types
 | 
				
			||||||
 | 
					  useImageHosting: {
 | 
				
			||||||
 | 
					    url: "",
 | 
				
			||||||
 | 
					    name: "",
 | 
				
			||||||
 | 
					    isSmmsOpen: true,
 | 
				
			||||||
 | 
					    isQiniuyunOpen: true,
 | 
				
			||||||
 | 
					    isAliyunOpen: true,
 | 
				
			||||||
 | 
					    isGiteeOpen: true,
 | 
				
			||||||
 | 
					    isGitHubOpen: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					Lib.propTypes = {
 | 
				
			||||||
 | 
					  defaultTitle: PropTypes.string,
 | 
				
			||||||
 | 
					  defaultText: PropTypes.string,
 | 
				
			||||||
 | 
					  onTextChange: PropTypes.func,
 | 
				
			||||||
 | 
					  onTextBlur: PropTypes.func,
 | 
				
			||||||
 | 
					  onTextFocus: PropTypes.func,
 | 
				
			||||||
 | 
					  onStyleChange: PropTypes.func,
 | 
				
			||||||
 | 
					  onStyleBlur: PropTypes.func,
 | 
				
			||||||
 | 
					  onStyleFocus: PropTypes.func,
 | 
				
			||||||
 | 
					  token: PropTypes.string,
 | 
				
			||||||
 | 
					  // eslint-disable-next-line react/require-default-props
 | 
				
			||||||
 | 
					  useImageHosting: PropTypes.shape({
 | 
				
			||||||
 | 
					    url: PropTypes.string,
 | 
				
			||||||
 | 
					    name: PropTypes.string,
 | 
				
			||||||
 | 
					    isSmmsOpen: PropTypes.bool,
 | 
				
			||||||
 | 
					    isQiniuyunOpen: PropTypes.bool,
 | 
				
			||||||
 | 
					    isAliyunOpen: PropTypes.bool,
 | 
				
			||||||
 | 
					    isGiteeOpen: PropTypes.bool,
 | 
				
			||||||
 | 
					    isGitHubOpen: PropTypes.bool,
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Lib;
 | 
				
			||||||
							
								
								
									
										100
									
								
								src/component/Dialog/AboutDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										100
									
								
								src/component/Dialog/AboutDialog.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Modal, Button} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class AboutDialog extends Component {
 | 
				
			||||||
 | 
					  handleOk = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setAboutOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCancel = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setAboutOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleVersion = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setAboutOpen(false);
 | 
				
			||||||
 | 
					    this.props.dialog.setVersionOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        title="关于"
 | 
				
			||||||
 | 
					        okText="确认"
 | 
				
			||||||
 | 
					        cancelText="取消"
 | 
				
			||||||
 | 
					        visible={this.props.dialog.isAboutOpen}
 | 
				
			||||||
 | 
					        onOk={this.handleOk}
 | 
				
			||||||
 | 
					        onCancel={this.handleCancel}
 | 
				
			||||||
 | 
					        footer={[
 | 
				
			||||||
 | 
					          <Button key="submit" type="primary" onClick={this.handleOk}>
 | 
				
			||||||
 | 
					            确认
 | 
				
			||||||
 | 
					          </Button>,
 | 
				
			||||||
 | 
					        ]}
 | 
				
			||||||
 | 
					        bodyStyle={{
 | 
				
			||||||
 | 
					          paddingTop: "5px",
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <h3 style={style.headerMargin}>
 | 
				
			||||||
 | 
					          Markdown2Html
 | 
				
			||||||
 | 
					          <a
 | 
				
			||||||
 | 
					            id="nice-about-dialog-star"
 | 
				
			||||||
 | 
					            rel="noopener noreferrer"
 | 
				
			||||||
 | 
					            target="_blank"
 | 
				
			||||||
 | 
					            href="https://github.com/shenweiyan/Markdown2Html"
 | 
				
			||||||
 | 
					            style={style.noBorder}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <img alt="" style={style.img} src="https://badgen.net/github/stars/shenweiyan/Markdown2Html" />
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					        </h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <p style={style.lineHeight}>支持自定义样式的 Markdown 编辑器;</p>
 | 
				
			||||||
 | 
					        <p style={style.lineHeight}>支持微信公众号、知乎和稀土掘金;</p>
 | 
				
			||||||
 | 
					        <p style={style.lineHeight}>
 | 
				
			||||||
 | 
					          如果你喜欢我们的工具,欢迎关注
 | 
				
			||||||
 | 
					          <a
 | 
				
			||||||
 | 
					            id="nice-about-dialog-github"
 | 
				
			||||||
 | 
					            rel="noopener noreferrer"
 | 
				
			||||||
 | 
					            target="_blank"
 | 
				
			||||||
 | 
					            href="https://github.com/shenweiyan/Markdown2Html"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					             GitHub 
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					        </p>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const style = {
 | 
				
			||||||
 | 
					  leftImgWidth: {
 | 
				
			||||||
 | 
					    width: "40%",
 | 
				
			||||||
 | 
					    height: "100%",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  rightImgWidth: {
 | 
				
			||||||
 | 
					    width: "60%",
 | 
				
			||||||
 | 
					    height: "100%",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  headerMargin: {
 | 
				
			||||||
 | 
					    marginTop: "5px",
 | 
				
			||||||
 | 
					    marginBottom: "5px",
 | 
				
			||||||
 | 
					    color: "black",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  lineHeight: {
 | 
				
			||||||
 | 
					    lineHeight: "26px",
 | 
				
			||||||
 | 
					    color: "black",
 | 
				
			||||||
 | 
					    padding: 0,
 | 
				
			||||||
 | 
					    margin: 0,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  img: {
 | 
				
			||||||
 | 
					    width: "70px",
 | 
				
			||||||
 | 
					    marginLeft: "10px",
 | 
				
			||||||
 | 
					    display: "inline-block",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  noBorder: {
 | 
				
			||||||
 | 
					    border: "none",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AboutDialog;
 | 
				
			||||||
							
								
								
									
										102
									
								
								src/component/Dialog/FormDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										102
									
								
								src/component/Dialog/FormDialog.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Modal, InputNumber, Form} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class FormDialog extends React.Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      ...initialState,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  buildRow = (rowNum, columnNum) => {
 | 
				
			||||||
 | 
					    let appendText = "|";
 | 
				
			||||||
 | 
					    if (rowNum === 1) {
 | 
				
			||||||
 | 
					      appendText += " --- |";
 | 
				
			||||||
 | 
					      for (let i = 0; i < columnNum - 1; i++) {
 | 
				
			||||||
 | 
					        appendText += " --- |";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      appendText += "     |";
 | 
				
			||||||
 | 
					      for (let i = 0; i < columnNum - 1; i++) {
 | 
				
			||||||
 | 
					        appendText += "     |";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return appendText + (/windows|win32/i.test(navigator.userAgent) ? "\r\n" : "\n");
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  buildFormFormat = (rowNum, columnNum) => {
 | 
				
			||||||
 | 
					    let formFormat = "";
 | 
				
			||||||
 | 
					    for (let i = 0; i < 3; i++) {
 | 
				
			||||||
 | 
					      formFormat += this.buildRow(i, columnNum);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (let i = 3; i <= rowNum; i++) {
 | 
				
			||||||
 | 
					      formFormat += this.buildRow(i, columnNum);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return formFormat;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleOk = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const cursor = markdownEditor.getCursor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const text = this.buildFormFormat(this.state.rowNum, this.state.columnNum);
 | 
				
			||||||
 | 
					    markdownEditor.replaceSelection(text, cursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.handleCancel();
 | 
				
			||||||
 | 
					    cursor.ch += 2;
 | 
				
			||||||
 | 
					    markdownEditor.setCursor(cursor);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCancel = () => {
 | 
				
			||||||
 | 
					    this.setState(initialState);
 | 
				
			||||||
 | 
					    this.props.dialog.setFormOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        title="添加表格"
 | 
				
			||||||
 | 
					        okText="确认"
 | 
				
			||||||
 | 
					        cancelText="取消"
 | 
				
			||||||
 | 
					        visible={this.props.dialog.isFormOpen}
 | 
				
			||||||
 | 
					        onOk={this.handleOk}
 | 
				
			||||||
 | 
					        onCancel={this.handleCancel}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Form.Item label="行数" labelCol={{span: 4}}>
 | 
				
			||||||
 | 
					          <InputNumber
 | 
				
			||||||
 | 
					            min={2}
 | 
				
			||||||
 | 
					            max={10}
 | 
				
			||||||
 | 
					            value={this.state.rowNum}
 | 
				
			||||||
 | 
					            defaultValue={1}
 | 
				
			||||||
 | 
					            onChange={(value) => this.setState({rowNum: value})}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="列数" labelCol={{span: 4}}>
 | 
				
			||||||
 | 
					          <InputNumber
 | 
				
			||||||
 | 
					            min={1}
 | 
				
			||||||
 | 
					            max={10}
 | 
				
			||||||
 | 
					            value={this.state.columnNum}
 | 
				
			||||||
 | 
					            defaultValue={1}
 | 
				
			||||||
 | 
					            onChange={(value) => this.setState({columnNum: value})}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const initialState = {
 | 
				
			||||||
 | 
					  columnNum: 1,
 | 
				
			||||||
 | 
					  rowNum: 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default FormDialog;
 | 
				
			||||||
							
								
								
									
										146
									
								
								src/component/Dialog/HistoryDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										146
									
								
								src/component/Dialog/HistoryDialog.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Modal, Empty, message} from "antd";
 | 
				
			||||||
 | 
					import LocalHistory from "../LocalHistory";
 | 
				
			||||||
 | 
					import {AutoSaveInterval, getLocalDocuments, setLocalDocuments, setLocalDraft} from "../LocalHistory/util";
 | 
				
			||||||
 | 
					import IndexDB from "../LocalHistory/indexdb";
 | 
				
			||||||
 | 
					import debouce from "lodash.debounce";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DocumentID = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class HistoryDialog extends Component {
 | 
				
			||||||
 | 
					  timer = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  db = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      documents: [],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async componentDidMount() {
 | 
				
			||||||
 | 
					    await this.initIndexDB();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentWillUnmount() {
 | 
				
			||||||
 | 
					    clearInterval(this.timer);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get editor() {
 | 
				
			||||||
 | 
					    return this.props.content.markdownEditor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  // async UNSAFE_componentWillReceiveProps(nextProps) {
 | 
				
			||||||
 | 
					  //   // 文档 id 变更
 | 
				
			||||||
 | 
					  //   if (this.props.documentID !== nextProps.documentID && nextProps.documentID != null) {
 | 
				
			||||||
 | 
					  //     if (this.db) {
 | 
				
			||||||
 | 
					  //       await this.overrideLocalDocuments(nextProps.documentID);
 | 
				
			||||||
 | 
					  //     }
 | 
				
			||||||
 | 
					  //   }
 | 
				
			||||||
 | 
					  // }
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  closeDialog = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setHistoryOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  editLocalDocument = (content) => {
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					    message.success("恢复成功!");
 | 
				
			||||||
 | 
					    this.closeDialog();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  autoSave = async (isRecent = false) => {
 | 
				
			||||||
 | 
					    const Content = this.props.content.markdownEditor.getValue();
 | 
				
			||||||
 | 
					    if (Content.trim() !== "") {
 | 
				
			||||||
 | 
					      const document = {
 | 
				
			||||||
 | 
					        Content,
 | 
				
			||||||
 | 
					        DocumentID: this.props.documentID,
 | 
				
			||||||
 | 
					        SaveTime: new Date(),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      const setLocalDocumentMethod = isRecent && this.state.documents.length > 0 ? setLocalDraft : setLocalDocuments;
 | 
				
			||||||
 | 
					      await setLocalDocumentMethod(this.db, this.state.documents, document);
 | 
				
			||||||
 | 
					      await this.overrideLocalDocuments(this.props.documentID);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async initIndexDB() {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const indexDB = new IndexDB({
 | 
				
			||||||
 | 
					        name: "mdnice-local-history",
 | 
				
			||||||
 | 
					        storeName: "customers",
 | 
				
			||||||
 | 
					        storeOptions: {keyPath: "id", autoIncrement: true},
 | 
				
			||||||
 | 
					        storeInit: (objectStore) => {
 | 
				
			||||||
 | 
					          objectStore.createIndex("DocumentID", "DocumentID", {unique: false});
 | 
				
			||||||
 | 
					          objectStore.createIndex("SaveTime", "SaveTime", {unique: false});
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      this.db = await indexDB.init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (this.db && this.props.documentID) {
 | 
				
			||||||
 | 
					        await this.overrideLocalDocuments(this.props.documentID);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // 每隔一段时间自动保存
 | 
				
			||||||
 | 
					      this.timer = setInterval(async () => {
 | 
				
			||||||
 | 
					        await this.autoSave();
 | 
				
			||||||
 | 
					      }, AutoSaveInterval);
 | 
				
			||||||
 | 
					      // 每改变内容自动保存最近的一条
 | 
				
			||||||
 | 
					      this.editor.on &&
 | 
				
			||||||
 | 
					        this.editor.on(
 | 
				
			||||||
 | 
					          "change",
 | 
				
			||||||
 | 
					          debouce(async () => {
 | 
				
			||||||
 | 
					            await this.autoSave(true);
 | 
				
			||||||
 | 
					          }, 1000),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      console.error(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 刷新本地历史文档
 | 
				
			||||||
 | 
					  async overrideLocalDocuments(documentID) {
 | 
				
			||||||
 | 
					    const localDocuments = await getLocalDocuments(this.db, +documentID);
 | 
				
			||||||
 | 
					    // console.log('refresh local',localDocuments);
 | 
				
			||||||
 | 
					    this.setState({
 | 
				
			||||||
 | 
					      documents: localDocuments,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        className="nice-md-local-history"
 | 
				
			||||||
 | 
					        title="本地历史"
 | 
				
			||||||
 | 
					        centered
 | 
				
			||||||
 | 
					        width={1080}
 | 
				
			||||||
 | 
					        visible={this.props.dialog.isHistoryOpen}
 | 
				
			||||||
 | 
					        onCancel={this.closeDialog}
 | 
				
			||||||
 | 
					        footer={null}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {this.state.documents && this.state.documents.length > 0 ? (
 | 
				
			||||||
 | 
					          <LocalHistory
 | 
				
			||||||
 | 
					            content={this.props.content.content}
 | 
				
			||||||
 | 
					            documents={this.state.documents}
 | 
				
			||||||
 | 
					            documentID={this.props.documentID}
 | 
				
			||||||
 | 
					            onEdit={this.editLocalDocument}
 | 
				
			||||||
 | 
					            onCancel={this.closeDialog}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        ) : (
 | 
				
			||||||
 | 
					          <Empty style={{width: "100%"}} description="暂无本地历史" />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HistoryDialog.defaultProps = {
 | 
				
			||||||
 | 
					  documentID: DocumentID,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default HistoryDialog;
 | 
				
			||||||
							
								
								
									
										180
									
								
								src/component/Dialog/ImageDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										180
									
								
								src/component/Dialog/ImageDialog.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,180 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Modal, Upload, Tabs, Select} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import AliOSS from "../ImageHosting/AliOSS";
 | 
				
			||||||
 | 
					import QiniuOSS from "../ImageHosting/QiniuOSS";
 | 
				
			||||||
 | 
					import Gitee from "../ImageHosting/Gitee";
 | 
				
			||||||
 | 
					import GitHub from "../ImageHosting/GitHub";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {uploadAdaptor} from "../../utils/imageHosting";
 | 
				
			||||||
 | 
					import {SM_MS_PROXY, IMAGE_HOSTING_TYPE, IMAGE_HOSTING_NAMES} from "../../utils/constant";
 | 
				
			||||||
 | 
					import appContext from "../../utils/appContext";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const {Dragger} = Upload;
 | 
				
			||||||
 | 
					const {TabPane} = Tabs;
 | 
				
			||||||
 | 
					const {Option} = Select;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class ImageDialog extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.images = [];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 确认后将内容更新到编辑器上
 | 
				
			||||||
 | 
					  handleOk = () => {
 | 
				
			||||||
 | 
					    let text = "";
 | 
				
			||||||
 | 
					    // 成功后添加url
 | 
				
			||||||
 | 
					    if (this.props.navbar.isContainImgName) {
 | 
				
			||||||
 | 
					      this.images.forEach((value) => {
 | 
				
			||||||
 | 
					        text += `\n`;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.images.forEach((value) => {
 | 
				
			||||||
 | 
					        text += `\n`;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 重新初始化
 | 
				
			||||||
 | 
					    this.images = [];
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const cursor = markdownEditor.getCursor();
 | 
				
			||||||
 | 
					    markdownEditor.replaceSelection(text, cursor);
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.props.dialog.setImageOpen(false);
 | 
				
			||||||
 | 
					    cursor.ch += 2;
 | 
				
			||||||
 | 
					    markdownEditor.setCursor(cursor);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCancel = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setImageOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  customRequest = ({action, data, file, headers, onError, onProgress, onSuccess, withCredentials}) => {
 | 
				
			||||||
 | 
					    const formData = new FormData();
 | 
				
			||||||
 | 
					    const {images} = this;
 | 
				
			||||||
 | 
					    if (data) {
 | 
				
			||||||
 | 
					      Object.keys(data).forEach((key) => {
 | 
				
			||||||
 | 
					        formData.append(key, data[key]);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 使用阿里云图床
 | 
				
			||||||
 | 
					    if (this.props.imageHosting.type === "阿里云") {
 | 
				
			||||||
 | 
					      uploadAdaptor({file, onSuccess, onError, images});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 使用七牛云图床
 | 
				
			||||||
 | 
					    else if (this.props.imageHosting.type === "七牛云") {
 | 
				
			||||||
 | 
					      uploadAdaptor({file, onSuccess, onError, onProgress, images});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 使用SM.MS图床
 | 
				
			||||||
 | 
					    else if (this.props.imageHosting.type === "SM.MS") {
 | 
				
			||||||
 | 
					      uploadAdaptor({formData, file, action, onProgress, onSuccess, onError, headers, withCredentials});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 使用Gitee图床
 | 
				
			||||||
 | 
					    else if (this.props.imageHosting.type === "Gitee") {
 | 
				
			||||||
 | 
					      uploadAdaptor({formData, file, action, onProgress, onSuccess, onError, headers, withCredentials, images});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 使用GitHub图床
 | 
				
			||||||
 | 
					    else if (this.props.imageHosting.type === "GitHub") {
 | 
				
			||||||
 | 
					      uploadAdaptor({formData, file, action, onProgress, onSuccess, onError, headers, withCredentials, images});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 使用用户提供的图床或是默认mdnice图床
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      uploadAdaptor({formData, file, onSuccess, onError, images});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      abort() {
 | 
				
			||||||
 | 
					        console.log("upload progress is aborted.");
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  typeChange = (type) => {
 | 
				
			||||||
 | 
					    this.props.imageHosting.setType(type);
 | 
				
			||||||
 | 
					    window.localStorage.setItem(IMAGE_HOSTING_TYPE, type);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {hostingList, type} = this.props.imageHosting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const columns = hostingList.map((option, index) => (
 | 
				
			||||||
 | 
					      <Option key={index} value={option.value}>
 | 
				
			||||||
 | 
					        {option.label}
 | 
				
			||||||
 | 
					      </Option>
 | 
				
			||||||
 | 
					    ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const imageHostingSwitch = (
 | 
				
			||||||
 | 
					      <Select style={{width: "90px"}} value={type} onChange={this.typeChange}>
 | 
				
			||||||
 | 
					        {columns}
 | 
				
			||||||
 | 
					      </Select>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        title="本地上传"
 | 
				
			||||||
 | 
					        okText="确认"
 | 
				
			||||||
 | 
					        cancelText="取消"
 | 
				
			||||||
 | 
					        visible={this.props.dialog.isImageOpen}
 | 
				
			||||||
 | 
					        onOk={this.handleOk}
 | 
				
			||||||
 | 
					        onCancel={this.handleCancel}
 | 
				
			||||||
 | 
					        bodyStyle={{paddingTop: "10px"}}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <appContext.Consumer>
 | 
				
			||||||
 | 
					          {({useImageHosting}) => (
 | 
				
			||||||
 | 
					            <Tabs tabBarExtraContent={imageHostingSwitch} type="card">
 | 
				
			||||||
 | 
					              <TabPane tab="图片上传" key="1">
 | 
				
			||||||
 | 
					                <Dragger name="file" multiple action={SM_MS_PROXY} customRequest={this.customRequest}>
 | 
				
			||||||
 | 
					                  <p className="ant-upload-drag-icon">
 | 
				
			||||||
 | 
					                    <SvgIcon name="inbox" style={style.svgIcon} fill="#40a9ff" />
 | 
				
			||||||
 | 
					                  </p>
 | 
				
			||||||
 | 
					                  <p className="ant-upload-text">点击或拖拽一张或多张照片上传</p>
 | 
				
			||||||
 | 
					                  <p className="ant-upload-hint">{"正在使用" + type + "图床"}</p>
 | 
				
			||||||
 | 
					                </Dragger>
 | 
				
			||||||
 | 
					              </TabPane>
 | 
				
			||||||
 | 
					              {useImageHosting.isAliyunOpen ? (
 | 
				
			||||||
 | 
					                <TabPane tab={IMAGE_HOSTING_NAMES.aliyun} key="2">
 | 
				
			||||||
 | 
					                  <AliOSS />
 | 
				
			||||||
 | 
					                </TabPane>
 | 
				
			||||||
 | 
					              ) : null}
 | 
				
			||||||
 | 
					              {useImageHosting.isQiniuyunOpen ? (
 | 
				
			||||||
 | 
					                <TabPane tab={IMAGE_HOSTING_NAMES.qiniuyun} key="3">
 | 
				
			||||||
 | 
					                  <QiniuOSS />
 | 
				
			||||||
 | 
					                </TabPane>
 | 
				
			||||||
 | 
					              ) : null}
 | 
				
			||||||
 | 
					              {useImageHosting.isGiteeOpen ? (
 | 
				
			||||||
 | 
					                <TabPane tab={IMAGE_HOSTING_NAMES.gitee} key="4">
 | 
				
			||||||
 | 
					                  <Gitee />
 | 
				
			||||||
 | 
					                </TabPane>
 | 
				
			||||||
 | 
					              ) : null}
 | 
				
			||||||
 | 
					              {useImageHosting.isGitHubOpen ? (
 | 
				
			||||||
 | 
					                <TabPane tab={IMAGE_HOSTING_NAMES.github} key="5">
 | 
				
			||||||
 | 
					                  <GitHub />
 | 
				
			||||||
 | 
					                </TabPane>
 | 
				
			||||||
 | 
					              ) : null}
 | 
				
			||||||
 | 
					            </Tabs>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </appContext.Consumer>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const style = {
 | 
				
			||||||
 | 
					  svgIcon: {
 | 
				
			||||||
 | 
					    width: "48px",
 | 
				
			||||||
 | 
					    height: "48px",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ImageDialog;
 | 
				
			||||||
							
								
								
									
										61
									
								
								src/component/Dialog/LinkDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										61
									
								
								src/component/Dialog/LinkDialog.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Modal, Input, Form} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class LinkDialog extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      link: "",
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleOk = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const cursor = markdownEditor.getCursor();
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    const text = `[${selection}](${this.state.link})`;
 | 
				
			||||||
 | 
					    markdownEditor.replaceSelection(text, cursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.setState({link: ""});
 | 
				
			||||||
 | 
					    this.props.dialog.setLinkOpen(false);
 | 
				
			||||||
 | 
					    cursor.ch += 1;
 | 
				
			||||||
 | 
					    markdownEditor.setCursor(cursor);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCancel = () => {
 | 
				
			||||||
 | 
					    this.setState({link: ""});
 | 
				
			||||||
 | 
					    this.props.dialog.setLinkOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleChange = (e) => {
 | 
				
			||||||
 | 
					    this.setState({link: e.target.value});
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        title="添加链接"
 | 
				
			||||||
 | 
					        okText="确认"
 | 
				
			||||||
 | 
					        cancelText="取消"
 | 
				
			||||||
 | 
					        visible={this.props.dialog.isLinkOpen}
 | 
				
			||||||
 | 
					        onOk={this.handleOk}
 | 
				
			||||||
 | 
					        onCancel={this.handleCancel}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Form.Item label="链接地址">
 | 
				
			||||||
 | 
					          <Input placeholder="请输入链接地址" value={this.state.link} onChange={this.handleChange} />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default LinkDialog;
 | 
				
			||||||
							
								
								
									
										106
									
								
								src/component/Dialog/SitDownDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										106
									
								
								src/component/Dialog/SitDownDialog.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,106 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Modal, Input, Select, message} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SitDownConverter from "../../utils/sitdownConverter";
 | 
				
			||||||
 | 
					import {SITDOWN_OPTIONS} from "../../utils/constant";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const {Option} = Select;
 | 
				
			||||||
 | 
					const {TextArea} = Input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class SitDownDialog extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      platform: "default",
 | 
				
			||||||
 | 
					      sourceCode: "",
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleOk = () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const {platform, sourceCode} = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const domParser = new DOMParser();
 | 
				
			||||||
 | 
					      const sourceCodeDom = domParser.parseFromString(sourceCode, "text/html");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let content = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (platform === "csdn") {
 | 
				
			||||||
 | 
					        const articleDom = sourceCodeDom.getElementById("content_views");
 | 
				
			||||||
 | 
					        content = SitDownConverter.CSDN(articleDom);
 | 
				
			||||||
 | 
					      } else if (platform === "juejin") {
 | 
				
			||||||
 | 
					        const articleDom = sourceCodeDom.getElementsByClassName("article-content");
 | 
				
			||||||
 | 
					        content = SitDownConverter.Juejin(articleDom[0]);
 | 
				
			||||||
 | 
					      } else if (platform === "zhihu") {
 | 
				
			||||||
 | 
					        const articleDom = sourceCodeDom.getElementsByClassName("Post-RichText");
 | 
				
			||||||
 | 
					        content = SitDownConverter.Zhihu(articleDom[0]);
 | 
				
			||||||
 | 
					      } else if (platform === "wechat") {
 | 
				
			||||||
 | 
					        const articleDom = sourceCodeDom.getElementById("js_content");
 | 
				
			||||||
 | 
					        content = SitDownConverter.Wechat(articleDom);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        content = SitDownConverter.GFM(sourceCodeDom);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.props.content.setContent(content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.props.dialog.setSitDownOpen(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					      // const cursor = markdownEditor.getCursor();
 | 
				
			||||||
 | 
					      // cursor.ch += 1;
 | 
				
			||||||
 | 
					      // markdownEditor.setCursor(cursor);
 | 
				
			||||||
 | 
					      markdownEditor.focus();
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      message.error("源代码与已选平台的文章域名不符");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCancel = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setSitDownOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handlePlatform = (value) => {
 | 
				
			||||||
 | 
					    this.setState({platform: value});
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleSourceCode = (e) => {
 | 
				
			||||||
 | 
					    this.setState({sourceCode: e.target.value});
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {sourceCode, platform} = this.state;
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        title="html 转 markdown"
 | 
				
			||||||
 | 
					        okText="转换"
 | 
				
			||||||
 | 
					        cancelText="取消"
 | 
				
			||||||
 | 
					        visible={this.props.dialog.isSitDownOpen}
 | 
				
			||||||
 | 
					        onOk={this.handleOk}
 | 
				
			||||||
 | 
					        onCancel={this.handleCancel}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Select value={platform} style={{width: 300, marginBottom: "20px"}} onChange={this.handlePlatform}>
 | 
				
			||||||
 | 
					          {SITDOWN_OPTIONS.map((option) => (
 | 
				
			||||||
 | 
					            <Option key={option.key} value={option.key}>
 | 
				
			||||||
 | 
					              {option.value}
 | 
				
			||||||
 | 
					            </Option>
 | 
				
			||||||
 | 
					          ))}
 | 
				
			||||||
 | 
					        </Select>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <TextArea
 | 
				
			||||||
 | 
					          rows={4}
 | 
				
			||||||
 | 
					          style={{marginBottom: "5px"}}
 | 
				
			||||||
 | 
					          value={sourceCode}
 | 
				
			||||||
 | 
					          onChange={this.handleSourceCode}
 | 
				
			||||||
 | 
					          placeholder="请放入网页源代码"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <span>提示:右键->显示网页源代码->全选->复制粘贴。</span>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SitDownDialog;
 | 
				
			||||||
							
								
								
									
										3
									
								
								src/component/Dialog/VersionDialog.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										3
									
								
								src/component/Dialog/VersionDialog.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					.specialInfo > * {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										155
									
								
								src/component/Dialog/VersionDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										155
									
								
								src/component/Dialog/VersionDialog.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,155 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Modal, Timeline, Button} from "antd";
 | 
				
			||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					import {NEWEST_VERSION} from "../../utils/constant";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./VersionDialog.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class VersionDialog extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      // eslint-disable-next-line react/no-unused-state
 | 
				
			||||||
 | 
					      versionNumber: 0,
 | 
				
			||||||
 | 
					      versionTimeline: [],
 | 
				
			||||||
 | 
					      recommend: null,
 | 
				
			||||||
 | 
					      specialInfo: "",
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleOk = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setVersionOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCancel = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setVersionOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleMore = () => {
 | 
				
			||||||
 | 
					    const w = window.open("about:blank");
 | 
				
			||||||
 | 
					    w.location.href = "https://git.luckday.cn/Markdown2Html/master/CHANGELOG.md";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleDocs = () => {
 | 
				
			||||||
 | 
					    const w = window.open("about:blank");
 | 
				
			||||||
 | 
					    w.location.href = "https://github.com/shenweiyan/Markdown2Html";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentDidMount = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const data = {
 | 
				
			||||||
 | 
					        versionId: 1,
 | 
				
			||||||
 | 
					        versionNumber: "1.0.0",
 | 
				
			||||||
 | 
					        versionTimeline: ["2023-09-20 增加网格黑主题", "2023-09-14 解决超链接文字复制到公众号颜色失效的问题", "2023-09-01 优化部分配置与信息", "2023-08-30 Fork 自 markdown2html"],
 | 
				
			||||||
 | 
					        recommend: {
 | 
				
			||||||
 | 
					          link: "https://github.com/shenweiyan/Knowledge-Garden",
 | 
				
			||||||
 | 
					          mainInfo: "欢迎关注我的知识花园",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        specialInfo: ''
 | 
				
			||||||
 | 
					        //specialInfo:
 | 
				
			||||||
 | 
					        //  '<div style="display:flex;justify-content:center;align-items:center;"><img style="width:50%;" src="http://md.aizhuanqian.online/img/wechat_qr.df324554.jpeg"/></div>',
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      const newestVersion = localStorage.getItem(NEWEST_VERSION);
 | 
				
			||||||
 | 
					      if (data.versionNumber !== newestVersion) {
 | 
				
			||||||
 | 
					        this.props.dialog.setVersionOpen(true);
 | 
				
			||||||
 | 
					        localStorage.setItem(NEWEST_VERSION, data.versionNumber);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.setState({...data});
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      console.error("读取最新版本信息错误");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        title="版本更新与说明"
 | 
				
			||||||
 | 
					        visible={this.props.dialog.isVersionOpen}
 | 
				
			||||||
 | 
					        onOk={this.handleOk}
 | 
				
			||||||
 | 
					        onCancel={this.handleCancel}
 | 
				
			||||||
 | 
					        footer={[
 | 
				
			||||||
 | 
					          <Button key="submit" type="primary" onClick={this.handleOk}>
 | 
				
			||||||
 | 
					            确认
 | 
				
			||||||
 | 
					          </Button>,
 | 
				
			||||||
 | 
					        ]}
 | 
				
			||||||
 | 
					        destroyOnClose
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Timeline>
 | 
				
			||||||
 | 
					          <Timeline.Item dot={<SvgIcon name="environment" style={style.svgIcon} />}>
 | 
				
			||||||
 | 
					              <strong>更多版本更新与说明信息请查看
 | 
				
			||||||
 | 
					                <a
 | 
				
			||||||
 | 
					                  id="more-info"
 | 
				
			||||||
 | 
					                  style={{fontWeight: "bold", borderBottom: "solid"}}
 | 
				
			||||||
 | 
					                  alt=""
 | 
				
			||||||
 | 
					                  href="https://github.com/shenweiyan/Markdown2Html"
 | 
				
			||||||
 | 
					                  rel="noopener noreferrer"
 | 
				
			||||||
 | 
					                  target="_blank"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  这里
 | 
				
			||||||
 | 
					                </a>
 | 
				
			||||||
 | 
					              </strong>
 | 
				
			||||||
 | 
					          </Timeline.Item>
 | 
				
			||||||
 | 
					          {this.state.versionTimeline.map((version, index) => {
 | 
				
			||||||
 | 
					            /*if (index === 0) {
 | 
				
			||||||
 | 
					              return (
 | 
				
			||||||
 | 
					                <Timeline.Item key={index} dot={<SvgIcon name="environment" style={style.svgIcon} />}>
 | 
				
			||||||
 | 
					                  <strong>{version}</strong>
 | 
				
			||||||
 | 
					                </Timeline.Item>
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              return <Timeline.Item key={index}>{version}</Timeline.Item>;
 | 
				
			||||||
 | 
					            }*/
 | 
				
			||||||
 | 
					            return <Timeline.Item key={index}>{version}</Timeline.Item>;
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					          <Timeline.Item>
 | 
				
			||||||
 | 
					            了解更多,请查看
 | 
				
			||||||
 | 
					            <a
 | 
				
			||||||
 | 
					              id="nice-version-dialog-doc"
 | 
				
			||||||
 | 
					              style={{fontWeight: "bold"}}
 | 
				
			||||||
 | 
					              alt=""
 | 
				
			||||||
 | 
					              href="https://github.com/shenweiyan/Markdown2Html"
 | 
				
			||||||
 | 
					              rel="noopener noreferrer"
 | 
				
			||||||
 | 
					              target="_blank"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              本仓库源码与说明
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					          </Timeline.Item>
 | 
				
			||||||
 | 
					          {this.state.recommend && (
 | 
				
			||||||
 | 
					            <Timeline.Item dot={<SvgIcon name="more" style={style.svgIcon} />}>
 | 
				
			||||||
 | 
					              <a
 | 
				
			||||||
 | 
					                id="nice-version-dialog-recommend"
 | 
				
			||||||
 | 
					                style={{fontWeight: "bold", borderBottom: "double"}}
 | 
				
			||||||
 | 
					                alt=""
 | 
				
			||||||
 | 
					                href={this.state.recommend.link}
 | 
				
			||||||
 | 
					                rel="noopener noreferrer"
 | 
				
			||||||
 | 
					                target="_blank"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                {this.state.recommend.mainInfo}
 | 
				
			||||||
 | 
					              </a>
 | 
				
			||||||
 | 
					            </Timeline.Item>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </Timeline>
 | 
				
			||||||
 | 
					        {this.state.specialInfo && (
 | 
				
			||||||
 | 
					          <div
 | 
				
			||||||
 | 
					            id="nice-version-dialog-special"
 | 
				
			||||||
 | 
					            dangerouslySetInnerHTML={{__html: this.state.specialInfo}}
 | 
				
			||||||
 | 
					            className="specialInfo"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const style = {
 | 
				
			||||||
 | 
					  svgIcon: {
 | 
				
			||||||
 | 
					    width: "16px",
 | 
				
			||||||
 | 
					    height: "16px",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default VersionDialog;
 | 
				
			||||||
							
								
								
									
										96
									
								
								src/component/ImageHosting/AliOSS.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										96
									
								
								src/component/ImageHosting/AliOSS.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Input, Form} from "antd";
 | 
				
			||||||
 | 
					import {ALIOSS_IMAGE_HOSTING} from "../../utils/constant";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const formItemLayout = {
 | 
				
			||||||
 | 
					  labelCol: {
 | 
				
			||||||
 | 
					    xs: {span: 6},
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  wrapperCol: {
 | 
				
			||||||
 | 
					    xs: {span: 16},
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class AliOSS extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    // 从localstorage里面读取
 | 
				
			||||||
 | 
					    const imageHosting = JSON.parse(localStorage.getItem(ALIOSS_IMAGE_HOSTING));
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      imageHosting,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  regionChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.region = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(ALIOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  accessKeyIdChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.accessKeyId = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(ALIOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  accessKeySecretChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.accessKeySecret = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(ALIOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bucketChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.bucket = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(ALIOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {region, accessKeyId, accessKeySecret, bucket} = this.state.imageHosting;
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Form {...formItemLayout}>
 | 
				
			||||||
 | 
					        <Form.Item label="Bucket" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={bucket} onChange={this.bucketChange} placeholder="例如:my-wechat" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="Region" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={region} onChange={this.regionChange} placeholder="例如:oss-cn-hangzhou" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="AccessKey ID" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={accessKeyId} onChange={this.accessKeyIdChange} placeholder="例如:qweASDF1234zxcvb" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="AccessKey Secret" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input
 | 
				
			||||||
 | 
					            value={accessKeySecret}
 | 
				
			||||||
 | 
					            onChange={this.accessKeySecretChange}
 | 
				
			||||||
 | 
					            placeholder="例如:qweASDF1234zxcvbqweASD"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="提示" style={style.formItem}>
 | 
				
			||||||
 | 
					          <span>配置后请在右上角进行切换,</span>
 | 
				
			||||||
 | 
					          <a
 | 
				
			||||||
 | 
					            rel="noopener noreferrer"
 | 
				
			||||||
 | 
					            target="_blank"
 | 
				
			||||||
 | 
					            href="https://preview.mdnice.com/article/developer/aliyun-image-hosting/"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            阿里云图床配置文档
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					      </Form>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const style = {
 | 
				
			||||||
 | 
					  formItem: {
 | 
				
			||||||
 | 
					    marginBottom: "10px",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AliOSS;
 | 
				
			||||||
							
								
								
									
										95
									
								
								src/component/ImageHosting/GitHub.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										95
									
								
								src/component/ImageHosting/GitHub.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Input, Form, Checkbox} from "antd";
 | 
				
			||||||
 | 
					import {GITHUB_IMAGE_HOSTING} from "../../utils/constant";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const formItemLayout = {
 | 
				
			||||||
 | 
					  labelCol: {
 | 
				
			||||||
 | 
					    xs: {span: 6},
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  wrapperCol: {
 | 
				
			||||||
 | 
					    xs: {span: 16},
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Gitee extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    // 从localstorage里面读取
 | 
				
			||||||
 | 
					    const imageHosting = JSON.parse(localStorage.getItem(GITHUB_IMAGE_HOSTING));
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      imageHosting,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  usernameChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.username = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(GITHUB_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  repoChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.repo = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(GITHUB_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tokenChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.token = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(GITHUB_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  jsdelivrChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.jsdelivr = e.target.checked ? "true" : "false";
 | 
				
			||||||
 | 
					    console.log(imageHosting);
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(GITHUB_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {username, repo, token, jsdelivr} = this.state.imageHosting;
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Form {...formItemLayout}>
 | 
				
			||||||
 | 
					        <Form.Item label="用户名" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={username} onChange={this.usernameChange} placeholder="例如:mdnice" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="仓库名" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={repo} onChange={this.repoChange} placeholder="例如:picture" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="token" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={token} onChange={this.tokenChange} placeholder="例如:qweASDF1234zxcvb" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="jsDelivr CDN" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Checkbox checked={jsdelivr === "true"} onChange={this.jsdelivrChange}>
 | 
				
			||||||
 | 
					            (强烈建议开启,加速图片)
 | 
				
			||||||
 | 
					          </Checkbox>
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="提示" style={style.formItem}>
 | 
				
			||||||
 | 
					          <span>配置后请在右上角进行切换,</span>
 | 
				
			||||||
 | 
					          <a
 | 
				
			||||||
 | 
					            rel="noopener noreferrer"
 | 
				
			||||||
 | 
					            target="_blank"
 | 
				
			||||||
 | 
					            href="https://preview.mdnice.com/article/developer/github-image-hosting/"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            GitHub图床配置文档
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					      </Form>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const style = {
 | 
				
			||||||
 | 
					  formItem: {
 | 
				
			||||||
 | 
					    marginBottom: "10px",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Gitee;
 | 
				
			||||||
							
								
								
									
										82
									
								
								src/component/ImageHosting/Gitee.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										82
									
								
								src/component/ImageHosting/Gitee.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Input, Form} from "antd";
 | 
				
			||||||
 | 
					import {GITEE_IMAGE_HOSTING} from "../../utils/constant";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const formItemLayout = {
 | 
				
			||||||
 | 
					  labelCol: {
 | 
				
			||||||
 | 
					    xs: {span: 6},
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  wrapperCol: {
 | 
				
			||||||
 | 
					    xs: {span: 16},
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Gitee extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    // 从localstorage里面读取
 | 
				
			||||||
 | 
					    const imageHosting = JSON.parse(localStorage.getItem(GITEE_IMAGE_HOSTING));
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      imageHosting,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  usernameChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.username = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(GITEE_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  repoChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.repo = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(GITEE_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tokenChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.token = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(GITEE_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {username, repo, token} = this.state.imageHosting;
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Form {...formItemLayout}>
 | 
				
			||||||
 | 
					        <Form.Item label="用户名" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={username} onChange={this.usernameChange} placeholder="例如:mdnice" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="仓库名" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={repo} onChange={this.repoChange} placeholder="例如:picture" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="token" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={token} onChange={this.tokenChange} placeholder="例如:qweASDF1234zxcvb" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="提示" style={style.formItem}>
 | 
				
			||||||
 | 
					          <span>配置后请在右上角进行切换,</span>
 | 
				
			||||||
 | 
					          <a
 | 
				
			||||||
 | 
					            rel="noopener noreferrer"
 | 
				
			||||||
 | 
					            target="_blank"
 | 
				
			||||||
 | 
					            href="https://preview.mdnice.com/article/developer/gitee-image-hosting/"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            Gitee 图床配置文档
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					      </Form>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const style = {
 | 
				
			||||||
 | 
					  formItem: {
 | 
				
			||||||
 | 
					    marginBottom: "10px",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Gitee;
 | 
				
			||||||
							
								
								
									
										124
									
								
								src/component/ImageHosting/QiniuOSS.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										124
									
								
								src/component/ImageHosting/QiniuOSS.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Input, Select, Form} from "antd";
 | 
				
			||||||
 | 
					import {QINIUOSS_IMAGE_HOSTING} from "../../utils/constant";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const {Option} = Select;
 | 
				
			||||||
 | 
					const formItemLayout = {
 | 
				
			||||||
 | 
					  labelCol: {
 | 
				
			||||||
 | 
					    xs: {span: 6},
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  wrapperCol: {
 | 
				
			||||||
 | 
					    xs: {span: 16},
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class QiniuOSS extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    // 从localstorage里面读取
 | 
				
			||||||
 | 
					    const imageHosting = JSON.parse(localStorage.getItem(QINIUOSS_IMAGE_HOSTING));
 | 
				
			||||||
 | 
					    const link = imageHosting.domain.split("://")[1];
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      imageHosting,
 | 
				
			||||||
 | 
					      link,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  regionChange = (value) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.region = value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(QINIUOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  accessKeyChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.accessKey = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(QINIUOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  secretKeyChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.secretKey = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(QINIUOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bucketChange = (e) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.bucket = e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(QINIUOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  linkChange = (e) => {
 | 
				
			||||||
 | 
					    this.setState({link: e.target.value});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.domain = "https://" + e.target.value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(QINIUOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  namespaceChange = ({target: {value}}) => {
 | 
				
			||||||
 | 
					    const {imageHosting} = this.state;
 | 
				
			||||||
 | 
					    imageHosting.namespace = value;
 | 
				
			||||||
 | 
					    this.setState({imageHosting});
 | 
				
			||||||
 | 
					    localStorage.setItem(QINIUOSS_IMAGE_HOSTING, JSON.stringify(imageHosting));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {region, accessKey, secretKey, bucket, namespace} = this.state.imageHosting;
 | 
				
			||||||
 | 
					    const {link} = this.state;
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Form {...formItemLayout}>
 | 
				
			||||||
 | 
					        <Form.Item label="存储空间名称" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={bucket} onChange={this.bucketChange} placeholder="例如:my-wechat" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="存储区域" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Select value={region} onChange={this.regionChange} placeholder="例如:qiniu.region.z2">
 | 
				
			||||||
 | 
					            <Option value="z0">华东</Option>
 | 
				
			||||||
 | 
					            <Option value="z1">华北</Option>
 | 
				
			||||||
 | 
					            <Option value="z2">华南</Option>
 | 
				
			||||||
 | 
					            <Option value="na0">北美</Option>
 | 
				
			||||||
 | 
					            <Option value="as0">东南亚</Option>
 | 
				
			||||||
 | 
					          </Select>
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="AccessKey" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={accessKey} onChange={this.accessKeyChange} placeholder="例如:qweASDF1234zxcvb" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="SecretKey" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={secretKey} onChange={this.secretKeyChange} placeholder="例如:qweASDF1234zxcvbqweASD" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="自定义域名" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={link} onChange={this.linkChange} addonBefore="https://" placeholder="例如:qiniu.mdnice.com" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="自定义命名空间" style={style.formItem}>
 | 
				
			||||||
 | 
					          <Input value={namespace} onChange={this.namespaceChange} placeholder="例如:image/" />
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					        <Form.Item label="提示" style={style.formItem}>
 | 
				
			||||||
 | 
					          <span>配置后请在右上角进行切换,</span>
 | 
				
			||||||
 | 
					          <a
 | 
				
			||||||
 | 
					            rel="noopener noreferrer"
 | 
				
			||||||
 | 
					            target="_blank"
 | 
				
			||||||
 | 
					            href="https://preview.mdnice.com/article/developer/qiniu-image-hosting/"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            七牛云图床配置文档
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					        </Form.Item>
 | 
				
			||||||
 | 
					      </Form>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const style = {
 | 
				
			||||||
 | 
					  formItem: {
 | 
				
			||||||
 | 
					    marginBottom: "10px",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default QiniuOSS;
 | 
				
			||||||
							
								
								
									
										113
									
								
								src/component/LocalHistory/index.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										113
									
								
								src/component/LocalHistory/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
				
			|||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					import {Menu, Button, Radio} from "antd";
 | 
				
			||||||
 | 
					import CodeMirror from "@uiw/react-codemirror";
 | 
				
			||||||
 | 
					import {diff_match_patch as DiffMatchPath} from "diff-match-patch";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./localHistory.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NOOP = () => {};
 | 
				
			||||||
 | 
					const prefix = "nice-md-local-history";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const diff = new DiffMatchPath();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LocalHistory extends React.Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    const {documents} = this.props;
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      content: documents[0].Content,
 | 
				
			||||||
 | 
					      selectedKeys: String(documents[0].id),
 | 
				
			||||||
 | 
					      mode: "all",
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getDiffHtml = () => {
 | 
				
			||||||
 | 
					    // eslint-disable-next-line no-underscore-dangle
 | 
				
			||||||
 | 
					    var a = diff.diff_linesToChars_(this.state.content, this.props.content);
 | 
				
			||||||
 | 
					    var lineText1 = a.chars1;
 | 
				
			||||||
 | 
					    var lineText2 = a.chars2;
 | 
				
			||||||
 | 
					    var diffs = diff.diff_main(lineText1, lineText2, false);
 | 
				
			||||||
 | 
					    // eslint-disable-next-line no-underscore-dangle
 | 
				
			||||||
 | 
					    diff.diff_charsToLines_(diffs, a.lineArray);
 | 
				
			||||||
 | 
					    const html = diff
 | 
				
			||||||
 | 
					      .diff_prettyHtml(diffs)
 | 
				
			||||||
 | 
					      .replace(/¶/g, "")
 | 
				
			||||||
 | 
					      .replace(/<br>/g, "​<br>​");
 | 
				
			||||||
 | 
					    return html;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  selectNav = ({selectedKeys}) => {
 | 
				
			||||||
 | 
					    const {Content: content} = this.props.documents.find((doc) => String(doc.id) === String(selectedKeys[0])) || {};
 | 
				
			||||||
 | 
					    this.setState({
 | 
				
			||||||
 | 
					      content,
 | 
				
			||||||
 | 
					      selectedKeys,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleModeChange = (e) => {
 | 
				
			||||||
 | 
					    this.setState({
 | 
				
			||||||
 | 
					      mode: e.target.value,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {documents} = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <>
 | 
				
			||||||
 | 
					        <Menu className={`${prefix}-nav`} onSelect={this.selectNav} selectedKeys={this.state.selectedKeys}>
 | 
				
			||||||
 | 
					          {documents.map((d) => (
 | 
				
			||||||
 | 
					            <Menu.Item key={d.id}>{d.SaveTime.toLocaleString()}</Menu.Item>
 | 
				
			||||||
 | 
					          ))}
 | 
				
			||||||
 | 
					        </Menu>
 | 
				
			||||||
 | 
					        {this.state.content && (
 | 
				
			||||||
 | 
					          <div className={`${prefix}-preview`}>
 | 
				
			||||||
 | 
					            {this.state.mode === "all" ? (
 | 
				
			||||||
 | 
					              <CodeMirror
 | 
				
			||||||
 | 
					                key="local-history"
 | 
				
			||||||
 | 
					                value={this.state.content}
 | 
				
			||||||
 | 
					                height="calc(100% - 56px)"
 | 
				
			||||||
 | 
					                options={{
 | 
				
			||||||
 | 
					                  readOnly: true,
 | 
				
			||||||
 | 
					                  theme: "md-mirror",
 | 
				
			||||||
 | 
					                  mode: "markdown",
 | 
				
			||||||
 | 
					                  lineWrapping: true,
 | 
				
			||||||
 | 
					                  lineNumbers: false,
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              <div dangerouslySetInnerHTML={{__html: this.getDiffHtml()}} className={`${prefix}-diff-content`} />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					            <div className={`${prefix}-btn-group`}>
 | 
				
			||||||
 | 
					              <Radio.Group onChange={this.handleModeChange} value={this.state.mode}>
 | 
				
			||||||
 | 
					                <Radio value="all">全文</Radio>
 | 
				
			||||||
 | 
					                <Radio value="diff">和当前内容对比</Radio>
 | 
				
			||||||
 | 
					              </Radio.Group>
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                <Button onClick={this.props.onCancel}>取消</Button>
 | 
				
			||||||
 | 
					                <Button
 | 
				
			||||||
 | 
					                  id="nice-local-history-review"
 | 
				
			||||||
 | 
					                  type="primary"
 | 
				
			||||||
 | 
					                  onClick={() => {
 | 
				
			||||||
 | 
					                    this.props.onEdit(this.state.content);
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  恢复此版本
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LocalHistory.defaultProps = {
 | 
				
			||||||
 | 
					  visible: false,
 | 
				
			||||||
 | 
					  document: [{}],
 | 
				
			||||||
 | 
					  onEdit: NOOP,
 | 
				
			||||||
 | 
					  onCancel: NOOP,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default LocalHistory;
 | 
				
			||||||
							
								
								
									
										63
									
								
								src/component/LocalHistory/indexdb.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										63
									
								
								src/component/LocalHistory/indexdb.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import {message} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// In the following line, you should include the prefixes of implementations you want to test.
 | 
				
			||||||
 | 
					const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
 | 
				
			||||||
 | 
					// DON'T use "var indexedDB = ..." if you're not in a function.
 | 
				
			||||||
 | 
					// Moreover, you may need references to some window.IDB* objects:
 | 
				
			||||||
 | 
					// const IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
 | 
				
			||||||
 | 
					// const IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class IndexDB {
 | 
				
			||||||
 | 
					  constructor(options = {}) {
 | 
				
			||||||
 | 
					    this.options = options;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async init() {
 | 
				
			||||||
 | 
					    if (!indexedDB) {
 | 
				
			||||||
 | 
					      message.error("初始化 indexdb 失败!浏览器不支持");
 | 
				
			||||||
 | 
					      throw Error("浏览器不支持 indexdb");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const {name, storeName = "", storeOptions = {}, storeInit = () => {}} = this.options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.storeName = storeName;
 | 
				
			||||||
 | 
					    this.storeOptions = storeOptions;
 | 
				
			||||||
 | 
					    this.storeInit = storeInit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const request = indexedDB.open(name);
 | 
				
			||||||
 | 
					    const result = await this.initEvent(request);
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  initEvent(request) {
 | 
				
			||||||
 | 
					    return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					      request.onerror = (event) => {
 | 
				
			||||||
 | 
					        // Do something with request.errorCode!
 | 
				
			||||||
 | 
					        message.error("初始化数据库失败!", event.target.errorCode);
 | 
				
			||||||
 | 
					        reject(new Error("初始化数据库失败!"));
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      request.onsuccess = (event) => {
 | 
				
			||||||
 | 
					        const db = event.target.result;
 | 
				
			||||||
 | 
					        console.log("成功初始化数据库");
 | 
				
			||||||
 | 
					        // this.db = db;
 | 
				
			||||||
 | 
					        resolve(db);
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 该事件仅在较新的浏览器中被实现
 | 
				
			||||||
 | 
					      request.onupgradeneeded = (event) => {
 | 
				
			||||||
 | 
					        // 更新对象存储空间和索引 ....
 | 
				
			||||||
 | 
					        const db = event.target.result;
 | 
				
			||||||
 | 
					        this.initStore(db, this.storeName, this.storeOptions, this.storeInit);
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  initStore(db, name, options, func) {
 | 
				
			||||||
 | 
					    // 创建一个对象存储空间来持有信息。
 | 
				
			||||||
 | 
					    const objectStore = db.createObjectStore(name, options);
 | 
				
			||||||
 | 
					    if (func) func(objectStore);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/component/LocalHistory/localHistory.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										66
									
								
								src/component/LocalHistory/localHistory.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					.nice-md-local-history .ant-modal-body {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    height: calc(100vh - 112px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-local-history .CodeMirror-merge,
 | 
				
			||||||
 | 
					.nice-md-local-history .CodeMirror-merge .CodeMirror {
 | 
				
			||||||
 | 
					    height: calc(100vh - 216px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-local-history .CodeMirror-merge-copy {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-local-history-preview {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    max-width: 776px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-local-history-btn-group {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    justify-content: space-between;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    margin-left: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-md-local-history-btn-group button {
 | 
				
			||||||
 | 
					    margin: 25px 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-local-history-btn-group button:last-child {
 | 
				
			||||||
 | 
					    margin-right: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-local-history-nav {
 | 
				
			||||||
 | 
					    width: 256px;
 | 
				
			||||||
 | 
					    flex: 0 0 auto;
 | 
				
			||||||
 | 
					    background: #fff;
 | 
				
			||||||
 | 
					    border-radius: 4px 0 0 4px;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    overflow-y: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-local-history-nav .ant-list-item {
 | 
				
			||||||
 | 
					    border-bottom: 0;
 | 
				
			||||||
 | 
					    padding: 17px 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-md-local-history-diff-content {
 | 
				
			||||||
 | 
					    width: inherit;
 | 
				
			||||||
 | 
					    background-color: rgba(245, 245, 245, 1);
 | 
				
			||||||
 | 
					    overflow-y: scroll;
 | 
				
			||||||
 | 
					    padding: 24px;
 | 
				
			||||||
 | 
					    height: calc(100% - 56px);
 | 
				
			||||||
 | 
					    color: #202020;
 | 
				
			||||||
 | 
					    border-radius: 5px;
 | 
				
			||||||
 | 
					    font-size: 16px;
 | 
				
			||||||
 | 
					    font-family: 'source-code-pro', Menlo, 'Courier New', Consolas, monospace, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
 | 
				
			||||||
 | 
					    font-variant-caps: normal;
 | 
				
			||||||
 | 
					    font-variant-east-asian: normal;
 | 
				
			||||||
 | 
					    font-variant-ligatures: contextual;
 | 
				
			||||||
 | 
					    line-height: 25px;
 | 
				
			||||||
 | 
					    font-weight: 400;
 | 
				
			||||||
 | 
					    font-variant-numeric: tabular-nums;
 | 
				
			||||||
 | 
					    font-feature-settings: "calt";
 | 
				
			||||||
 | 
					    -webkit-font-smoothing: antialiased;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										106
									
								
								src/component/LocalHistory/util.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										106
									
								
								src/component/LocalHistory/util.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,106 @@
 | 
				
			|||||||
 | 
					import {message} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function saveTimeSort(a, b) {
 | 
				
			||||||
 | 
					  return new Date(b.SaveTime).getTime() - new Date(a.SaveTime).getTime();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MaxLocalDocumentLength = 30;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// export const AutoSaveInterval = 10 * 60 * 1000;
 | 
				
			||||||
 | 
					export const AutoSaveInterval = 60 * 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getLocalDocuments = (db, DocumentID) => {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const transaction = db.transaction(["customers"], "readonly");
 | 
				
			||||||
 | 
					    const store = transaction.objectStore("customers");
 | 
				
			||||||
 | 
					    const keyRange = IDBKeyRange.only(DocumentID);
 | 
				
			||||||
 | 
					    const index = store.index("DocumentID");
 | 
				
			||||||
 | 
					    const req = index.openCursor(keyRange);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					      const result = [];
 | 
				
			||||||
 | 
					      req.onsuccess = (event) => {
 | 
				
			||||||
 | 
					        const cursor = event.target.result;
 | 
				
			||||||
 | 
					        if (cursor) {
 | 
				
			||||||
 | 
					          // Do something with the matches.
 | 
				
			||||||
 | 
					          result.push(cursor.value);
 | 
				
			||||||
 | 
					          cursor.continue();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          // console.log('获取成功 ');
 | 
				
			||||||
 | 
					          result.sort(saveTimeSort);
 | 
				
			||||||
 | 
					          resolve(result);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      req.onerror = (event) => {
 | 
				
			||||||
 | 
					        console.error("获取失败");
 | 
				
			||||||
 | 
					        reject(event);
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
					    console.log("未知错误", DocumentID);
 | 
				
			||||||
 | 
					    return Promise.reject(e);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const setLocalDocuments = (db, localDocuments, document = {}) => {
 | 
				
			||||||
 | 
					  const draftIndex = 0;
 | 
				
			||||||
 | 
					  if (localDocuments[draftIndex + 1] && localDocuments[draftIndex + 1].Content === localDocuments[draftIndex].Content) {
 | 
				
			||||||
 | 
					    // console.log("内容未更新,不进行本地保存。");
 | 
				
			||||||
 | 
					    return Promise.resolve();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					    const transaction = db.transaction(["customers"], "readwrite");
 | 
				
			||||||
 | 
					    const store = transaction.objectStore("customers");
 | 
				
			||||||
 | 
					    let req = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Info: 长度超过用 put,没超过用 add
 | 
				
			||||||
 | 
					    if (localDocuments.length >= MaxLocalDocumentLength) {
 | 
				
			||||||
 | 
					      const {id} = localDocuments.sort(saveTimeSort)[localDocuments.length - 1];
 | 
				
			||||||
 | 
					      req = store.put({
 | 
				
			||||||
 | 
					        ...document,
 | 
				
			||||||
 | 
					        id,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      req = store.add(document);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    req.onsuccess = () => {
 | 
				
			||||||
 | 
					      // console.log("自动保存成功");
 | 
				
			||||||
 | 
					      resolve();
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    req.onerror = (event) => {
 | 
				
			||||||
 | 
					      message.error("自动保存失败");
 | 
				
			||||||
 | 
					      reject(event);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const setLocalDraft = (db, localDocuments, document = {}) => {
 | 
				
			||||||
 | 
					  const draft = localDocuments[0];
 | 
				
			||||||
 | 
					  if (draft && document.Content === draft.Content) {
 | 
				
			||||||
 | 
					    console.log("草稿未更新,不进行本地保存。");
 | 
				
			||||||
 | 
					    return Promise.resolve();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					    const transaction = db.transaction(["customers"], "readwrite");
 | 
				
			||||||
 | 
					    const store = transaction.objectStore("customers");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Info: 更新草稿
 | 
				
			||||||
 | 
					    const {id} = draft;
 | 
				
			||||||
 | 
					    const req = store.put({
 | 
				
			||||||
 | 
					      ...document,
 | 
				
			||||||
 | 
					      id,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    req.onsuccess = () => {
 | 
				
			||||||
 | 
					      // console.log("自动保存草稿成功");
 | 
				
			||||||
 | 
					      resolve();
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    req.onerror = (event) => {
 | 
				
			||||||
 | 
					      message.error("自动保存草稿失败");
 | 
				
			||||||
 | 
					      reject(event);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										13
									
								
								src/component/MenuLeft/CodeTheme.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										13
									
								
								src/component/MenuLeft/CodeTheme.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					.nice-codetheme-item {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  padding: 5px 12px 5px 6px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-codetheme-item-name {
 | 
				
			||||||
 | 
					  margin-right: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-codetheme-item-flag {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  width: 16px;
 | 
				
			||||||
 | 
					  margin-right: 4px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/component/MenuLeft/CodeTheme.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										66
									
								
								src/component/MenuLeft/CodeTheme.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {CODE_OPTIONS, RIGHT_SYMBOL, IS_MAC_CODE} from "../../utils/constant";
 | 
				
			||||||
 | 
					import "./CodeTheme.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class CodeTheme extends React.Component {
 | 
				
			||||||
 | 
					  changeCodeTheme = (item) => {
 | 
				
			||||||
 | 
					    // 是否为 Mac 风格代码
 | 
				
			||||||
 | 
					    if (item.key === IS_MAC_CODE) {
 | 
				
			||||||
 | 
					      const {isMacCode, codeNum} = this.props.navbar;
 | 
				
			||||||
 | 
					      if (isMacCode) {
 | 
				
			||||||
 | 
					        this.props.navbar.setMacCode(false);
 | 
				
			||||||
 | 
					        this.props.navbar.setCodeNum(codeNum, false);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.props.navbar.setMacCode(true);
 | 
				
			||||||
 | 
					        this.props.navbar.setCodeNum(codeNum, true);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      const {isMacCode} = this.props.navbar;
 | 
				
			||||||
 | 
					      const codeNum = parseInt(item.key, 10);
 | 
				
			||||||
 | 
					      this.props.navbar.setCodeNum(codeNum, isMacCode);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {codeNum, isMacCode} = this.props.navbar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const codeMenu = (
 | 
				
			||||||
 | 
					      <Menu onClick={this.changeCodeTheme}>
 | 
				
			||||||
 | 
					        {CODE_OPTIONS.map((option, index) => (
 | 
				
			||||||
 | 
					          <Menu.Item key={index}>
 | 
				
			||||||
 | 
					            <div id={`nice-menu-codetheme-${option.id}`} className="nice-codetheme-item">
 | 
				
			||||||
 | 
					              <span>
 | 
				
			||||||
 | 
					                <span className="nice-codetheme-item-flag">{codeNum === index && <span>{RIGHT_SYMBOL}</span>}</span>
 | 
				
			||||||
 | 
					                <span className="nice-codetheme-item-name">{option.name}</span>
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </Menu.Item>
 | 
				
			||||||
 | 
					        ))}
 | 
				
			||||||
 | 
					        <Menu.Divider />
 | 
				
			||||||
 | 
					        <Menu.Item key={IS_MAC_CODE}>
 | 
				
			||||||
 | 
					          <div id="nice-menu-codetheme-apple" className="nice-codetheme-item">
 | 
				
			||||||
 | 
					            <span>
 | 
				
			||||||
 | 
					              <span className="nice-codetheme-item-flag">{isMacCode && <span>{RIGHT_SYMBOL}</span>}</span>
 | 
				
			||||||
 | 
					              <span className="nice-codetheme-item-name">Mac 风格</span>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </Menu.Item>
 | 
				
			||||||
 | 
					      </Menu>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={codeMenu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a id="nice-menu-codetheme" className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          代码主题
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default CodeTheme;
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/component/MenuLeft/File.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										27
									
								
								src/component/MenuLeft/File.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ImportFile from "./File/ImportFile";
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const menu = (
 | 
				
			||||||
 | 
					  <Menu>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <ImportFile />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					  </Menu>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class File extends Component {
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={menu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a id="nice-menu-file" className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          文件
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default File;
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/component/MenuLeft/File/ImportFile.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										40
									
								
								src/component/MenuLeft/File/ImportFile.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					import {message} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class ImportFile extends Component {
 | 
				
			||||||
 | 
					  handleChange = (e) => {
 | 
				
			||||||
 | 
					    const file = e.target.files[0];
 | 
				
			||||||
 | 
					    const reader = new FileReader();
 | 
				
			||||||
 | 
					    reader.onload = (event) => {
 | 
				
			||||||
 | 
					      this.props.content.setContent(event.target.result);
 | 
				
			||||||
 | 
					      message.success("导入文件成功!");
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    reader.readAsText(file);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <label id="nice-menu-import-file" className="nice-menu-item" htmlFor="importFile">
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">导入</span>
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            style={{display: "none"}}
 | 
				
			||||||
 | 
					            type="file"
 | 
				
			||||||
 | 
					            id="importFile"
 | 
				
			||||||
 | 
					            accept=".txt,.md"
 | 
				
			||||||
 | 
					            hidden=""
 | 
				
			||||||
 | 
					            onChange={this.handleChange}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </label>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ImportFile;
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/component/MenuLeft/Function.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										43
									
								
								src/component/MenuLeft/Function.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Reset from "./Function/Reset";
 | 
				
			||||||
 | 
					import Search from "./Function/Search";
 | 
				
			||||||
 | 
					import History from "./Function/History";
 | 
				
			||||||
 | 
					import SitDown from "./Function/SitDown";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const menu = (
 | 
				
			||||||
 | 
					  <Menu>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Reset />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Search />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Menu.Divider />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <History />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <SitDown />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					  </Menu>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Function extends Component {
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={menu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a id="nice-menu-function" className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          功能
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Function;
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/component/MenuLeft/Function/History.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										25
									
								
								src/component/MenuLeft/Function/History.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class History extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setHistoryOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-history" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">本地历史</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default History;
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/component/MenuLeft/Function/Reset.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										42
									
								
								src/component/MenuLeft/Function/Reset.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Modal, message} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import TEMPLATE from "../../../template/index";
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Reset extends Component {
 | 
				
			||||||
 | 
					  showConfirm = () => {
 | 
				
			||||||
 | 
					    Modal.confirm({
 | 
				
			||||||
 | 
					      title: "确认重置么?",
 | 
				
			||||||
 | 
					      content: "重置后将丢失本地保存的文本和自定义样式",
 | 
				
			||||||
 | 
					      okText: "确定",
 | 
				
			||||||
 | 
					      okType: "danger",
 | 
				
			||||||
 | 
					      cancelText: "取消",
 | 
				
			||||||
 | 
					      onOk: () => {
 | 
				
			||||||
 | 
					        this.props.content.setContent(TEMPLATE.content);
 | 
				
			||||||
 | 
					        this.props.content.setStyle(TEMPLATE.normal);
 | 
				
			||||||
 | 
					        this.props.content.setCustomStyle(TEMPLATE.custom);
 | 
				
			||||||
 | 
					        this.props.navbar.setTemplateNum(0);
 | 
				
			||||||
 | 
					        message.success("重置成功!");
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      onCancel() {},
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-reset" className="nice-menu-item" onClick={this.showConfirm}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">重置</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Reset;
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/component/MenuLeft/Function/Search.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										28
									
								
								src/component/MenuLeft/Function/Search.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Search extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setSearchOpen(!this.props.dialog.isSearchOpen);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-search" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">查找</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.search}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Search;
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/component/MenuLeft/Function/SitDown.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										25
									
								
								src/component/MenuLeft/Function/SitDown.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class SitDownFunction extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setSitDownOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-sitdown" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">html转markdown</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SitDownFunction;
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/component/MenuLeft/Help.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										40
									
								
								src/component/MenuLeft/Help.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import About from "./Help/About";
 | 
				
			||||||
 | 
					import Version from "./Help/Version";
 | 
				
			||||||
 | 
					import Document from "./Help/Document";
 | 
				
			||||||
 | 
					import Question from "./Help/Question";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const menu = (
 | 
				
			||||||
 | 
					  <Menu>
 | 
				
			||||||
 | 
					    {/*<Menu.Item>*/}
 | 
				
			||||||
 | 
					    {/*  <About />*/}
 | 
				
			||||||
 | 
					    {/*</Menu.Item>*/}
 | 
				
			||||||
 | 
					    {/*<Menu.Item>*/}
 | 
				
			||||||
 | 
					    {/*  <Version />*/}
 | 
				
			||||||
 | 
					    {/*</Menu.Item>*/}
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Document />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    {/*<Menu.Item>*/}
 | 
				
			||||||
 | 
					    {/*  <Question />*/}
 | 
				
			||||||
 | 
					    {/*</Menu.Item>*/}
 | 
				
			||||||
 | 
					  </Menu>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Help extends Component {
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={menu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a id="nice-menu-help" className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          帮助
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Help;
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/component/MenuLeft/Help/About.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										25
									
								
								src/component/MenuLeft/Help/About.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class About extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setAboutOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-about" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">关于</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default About;
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/component/MenuLeft/Help/Document.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										23
									
								
								src/component/MenuLeft/Help/Document.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Document extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const w = window.open("about:blank");
 | 
				
			||||||
 | 
					    w.location.href = "https://doc.luckday.cn";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-document" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">更多文档</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Document;
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/component/MenuLeft/Help/Question.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										23
									
								
								src/component/MenuLeft/Help/Question.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Question extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const w = window.open("about:blank");
 | 
				
			||||||
 | 
					    w.location.href = "https://github.com/shenweiyan/Markdown2Html/issues/new";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-question" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">我要提问</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Question;
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/component/MenuLeft/Help/Version.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										25
									
								
								src/component/MenuLeft/Help/Version.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Version extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setVersionOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-version" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">版本信息</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Version;
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/component/MenuLeft/Help/Version.js.default
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										25
									
								
								src/component/MenuLeft/Help/Version.js.default
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Version extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setVersionOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-version" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">版本信息</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Version;
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/component/MenuLeft/Help/Version.js.new
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										23
									
								
								src/component/MenuLeft/Help/Version.js.new
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Question extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const w = window.open("about:blank");
 | 
				
			||||||
 | 
					    w.location.href = "";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-question" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">版本信息</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Version;
 | 
				
			||||||
							
								
								
									
										60
									
								
								src/component/MenuLeft/LogIn.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										60
									
								
								src/component/MenuLeft/LogIn.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import {Tooltip, Button} from "antd";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {CLIENT_ID, CLIENT_SECRET, PROXY, ACCESS_TOKEN, ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					import {queryParse, axiosJSON, axiosGithub} from "../../utils/helper";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("userInfo")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class LogIn extends React.Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.code = queryParse().code;
 | 
				
			||||||
 | 
					    if (this.code) {
 | 
				
			||||||
 | 
					      this.loginBack();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const TOKEN = localStorage.getItem(ACCESS_TOKEN);
 | 
				
			||||||
 | 
					    if (TOKEN) {
 | 
				
			||||||
 | 
					      axios.defaults.headers.common.Authorization = `token ${TOKEN}`;
 | 
				
			||||||
 | 
					      this.getUserInfo();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  login = () => {
 | 
				
			||||||
 | 
					    window.location.href = `https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}&scope=public_repo`;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  loginBack = async () => {
 | 
				
			||||||
 | 
					    const res = await axiosJSON.post(PROXY, {
 | 
				
			||||||
 | 
					      code: this.code,
 | 
				
			||||||
 | 
					      client_id: CLIENT_ID,
 | 
				
			||||||
 | 
					      client_secret: CLIENT_SECRET,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    localStorage.setItem(ACCESS_TOKEN, res.data.access_token);
 | 
				
			||||||
 | 
					    window.location.href = "/";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getUserInfo = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await axiosGithub.get(`/user`);
 | 
				
			||||||
 | 
					      this.props.userInfo.setUserInfo(res.data);
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip placement="bottom" mouseEnterDelay={ENTER_DELAY} mouseLeaveDelay={LEAVE_DELAY} title="登录">
 | 
				
			||||||
 | 
					        <Button shape="circle" className="nice-btn-login" onClick={this.login}>
 | 
				
			||||||
 | 
					          <SvgIcon name="github" className="nice-btn-login-icon" />
 | 
				
			||||||
 | 
					        </Button>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default LogIn;
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/component/MenuLeft/Login.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/component/MenuLeft/Login.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					.nice-btn-login {
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  width: 24px;
 | 
				
			||||||
 | 
					  height: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-btn-login-icon {
 | 
				
			||||||
 | 
					  width: 24px;
 | 
				
			||||||
 | 
					  height: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/component/MenuLeft/Paragraph.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										38
									
								
								src/component/MenuLeft/Paragraph.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const menu = (
 | 
				
			||||||
 | 
					  <Menu>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <a target="_blank" rel="noopener noreferrer" href="#">
 | 
				
			||||||
 | 
					        导出
 | 
				
			||||||
 | 
					      </a>
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <a target="_blank" rel="noopener noreferrer" href="#">
 | 
				
			||||||
 | 
					        导入
 | 
				
			||||||
 | 
					      </a>
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <a target="_blank" rel="noopener noreferrer" href="#">
 | 
				
			||||||
 | 
					        打印
 | 
				
			||||||
 | 
					      </a>
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					  </Menu>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Paragraph extends Component {
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={menu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          段落
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Paragraph;
 | 
				
			||||||
							
								
								
									
										74
									
								
								src/component/MenuLeft/Pattern.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										74
									
								
								src/component/MenuLeft/Pattern.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Bold from "./Pattern/Bold";
 | 
				
			||||||
 | 
					import Code from "./Pattern/Code";
 | 
				
			||||||
 | 
					import Del from "./Pattern/Del";
 | 
				
			||||||
 | 
					import Italic from "./Pattern/Italic";
 | 
				
			||||||
 | 
					import Link from "./Pattern/Link";
 | 
				
			||||||
 | 
					import Form from "./Pattern/Form";
 | 
				
			||||||
 | 
					import Image from "./Pattern/Image";
 | 
				
			||||||
 | 
					import Format from "./Pattern/Format";
 | 
				
			||||||
 | 
					import LinkToFoot from "./Pattern/LinkToFoot";
 | 
				
			||||||
 | 
					import Font from "./Pattern/Font";
 | 
				
			||||||
 | 
					import InlineCode from "./Pattern/InlineCode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const menu = (
 | 
				
			||||||
 | 
					  <Menu>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Del />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Bold />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Italic />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Code />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <InlineCode />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Menu.Divider />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Link />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Form />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Image />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Font />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Menu.Divider />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <LinkToFoot />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <Format />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					  </Menu>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Pattern extends Component {
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={menu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a id="nice-menu-pattern" className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          格式
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Pattern;
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/component/MenuLeft/Pattern/Bold.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										37
									
								
								src/component/MenuLeft/Pattern/Bold.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {bold} from "../../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Bold extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    bold(markdownEditor, selection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-bold" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">加粗</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.bold}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Bold;
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/component/MenuLeft/Pattern/Code.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										36
									
								
								src/component/MenuLeft/Pattern/Code.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {code} from "../../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Code extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    code(markdownEditor, selection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-code" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">代码</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.code}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Code;
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/component/MenuLeft/Pattern/Del.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										36
									
								
								src/component/MenuLeft/Pattern/Del.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {del} from "../../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Del extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    del(markdownEditor, selection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-del" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">删除线</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.del}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Del;
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/component/MenuLeft/Pattern/Font.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										43
									
								
								src/component/MenuLeft/Pattern/Font.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {message} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {FONT_THEME_ID, RIGHT_SYMBOL} from "../../../utils/constant";
 | 
				
			||||||
 | 
					import {replaceStyle} from "../../../utils/helper";
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Font extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      isSerif: false,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 衬线字体 和 非衬线字体 切换
 | 
				
			||||||
 | 
					  toggleFont = () => {
 | 
				
			||||||
 | 
					    const {isSerif} = this.state;
 | 
				
			||||||
 | 
					    const serif = `#nice { 
 | 
				
			||||||
 | 
					      font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
 | 
				
			||||||
 | 
					    }`;
 | 
				
			||||||
 | 
					    const sansSerif = `#nice { 
 | 
				
			||||||
 | 
					      font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, 'Open Sans', 'Helvetica Neue', sans-serif;
 | 
				
			||||||
 | 
					    }`;
 | 
				
			||||||
 | 
					    const choosen = isSerif ? serif : sansSerif;
 | 
				
			||||||
 | 
					    replaceStyle(FONT_THEME_ID, choosen);
 | 
				
			||||||
 | 
					    message.success("字体切换成功!");
 | 
				
			||||||
 | 
					    this.setState({isSerif: !isSerif});
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-font" className="nice-menu-item" onClick={this.toggleFont}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag">{!this.state.isSerif && <span>{RIGHT_SYMBOL}</span>}</span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">衬线字体</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Font;
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/component/MenuLeft/Pattern/Form.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										28
									
								
								src/component/MenuLeft/Pattern/Form.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {inject, observer} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Form extends Component {
 | 
				
			||||||
 | 
					  showModal = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setFormOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-form" className="nice-menu-item" onClick={this.showModal}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">表格</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.form}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Form;
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/component/MenuLeft/Pattern/Format.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										30
									
								
								src/component/MenuLeft/Pattern/Format.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {formatDoc} from "../../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Format extends Component {
 | 
				
			||||||
 | 
					  handleFormat = () => {
 | 
				
			||||||
 | 
					    const {content} = this.props.content;
 | 
				
			||||||
 | 
					    formatDoc(content, this.props.content);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-format" className="nice-menu-item" onClick={this.handleFormat}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">格式化文档</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.format}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Format;
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/component/MenuLeft/Pattern/Image.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										28
									
								
								src/component/MenuLeft/Pattern/Image.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Image extends Component {
 | 
				
			||||||
 | 
					  showModal = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setImageOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-image" className="nice-menu-item" onClick={this.showModal}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">图片</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.image}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Image;
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/component/MenuLeft/Pattern/InlineCode.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										36
									
								
								src/component/MenuLeft/Pattern/InlineCode.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {inlineCode} from "../../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class InlineCode extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    inlineCode(markdownEditor, selection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-inline-code" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">行内代码</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.inlineCode}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default InlineCode;
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/component/MenuLeft/Pattern/Italic.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										36
									
								
								src/component/MenuLeft/Pattern/Italic.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {italic} from "../../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Italic extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    italic(markdownEditor, selection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-italic" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">倾斜</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.italic}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Italic;
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/component/MenuLeft/Pattern/Link.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										28
									
								
								src/component/MenuLeft/Pattern/Link.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Link extends Component {
 | 
				
			||||||
 | 
					  showModal = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setLinkOpen(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-link" className="nice-menu-item" onClick={this.showModal}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">链接</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.link}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Link;
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/component/MenuLeft/Pattern/LinkToFoot.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										30
									
								
								src/component/MenuLeft/Pattern/LinkToFoot.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {parseLinkToFoot} from "../../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Format extends Component {
 | 
				
			||||||
 | 
					  handleFormat = () => {
 | 
				
			||||||
 | 
					    const {content} = this.props.content;
 | 
				
			||||||
 | 
					    parseLinkToFoot(content, this.props.content);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-link-to-foot" className="nice-menu-item" onClick={this.handleFormat}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">微信外链转脚注</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span className="nice-menu-shortcut">{hotKeys.linkToFoot}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Format;
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/component/MenuLeft/Setting.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										32
									
								
								src/component/MenuLeft/Setting.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SyncScroll from "./Setting/SyncScroll";
 | 
				
			||||||
 | 
					import ContainImgName from "./Setting/ContainImgName";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const menu = (
 | 
				
			||||||
 | 
					  <Menu>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <SyncScroll />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <ContainImgName />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					  </Menu>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Setting extends Component {
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={menu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a id="nice-menu-setting" className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          设置
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Setting;
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/component/MenuLeft/Setting/ContainImgName.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										27
									
								
								src/component/MenuLeft/Setting/ContainImgName.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {RIGHT_SYMBOL} from "../../../utils/constant";
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class SyncScroll extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {isContainImgName} = this.props.navbar;
 | 
				
			||||||
 | 
					    this.props.navbar.setContainImgName(!isContainImgName);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-contain-img-name" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag">{this.props.navbar.isContainImgName && <span>{RIGHT_SYMBOL}</span>}</span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">上传图片时包含名称</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SyncScroll;
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/component/MenuLeft/Setting/SyncScroll.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										27
									
								
								src/component/MenuLeft/Setting/SyncScroll.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {RIGHT_SYMBOL} from "../../../utils/constant";
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class SyncScroll extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {isSyncScroll} = this.props.navbar;
 | 
				
			||||||
 | 
					    this.props.navbar.setSyncScroll(!isSyncScroll);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-sync-scroll" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag">{this.props.navbar.isSyncScroll && <span>{RIGHT_SYMBOL}</span>}</span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">同步滚动</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SyncScroll;
 | 
				
			||||||
							
								
								
									
										54
									
								
								src/component/MenuLeft/Theme.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										54
									
								
								src/component/MenuLeft/Theme.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					.nice-themeselect-md-cutom-menu {
 | 
				
			||||||
 | 
					  margin-left: 8px;
 | 
				
			||||||
 | 
					  border: 1px dashed #1890ff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-md-menu {
 | 
				
			||||||
 | 
					  margin-left: 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-code-menu {
 | 
				
			||||||
 | 
					  margin-left: 8px;
 | 
				
			||||||
 | 
					  margin-right: 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-theme-item {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  padding: 5px 12px 5px 6px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-theme-item-author {
 | 
				
			||||||
 | 
					  color: gray;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-theme-item-name {
 | 
				
			||||||
 | 
					  margin-right: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-theme-item-new {
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  color: #1890ff;
 | 
				
			||||||
 | 
					  background: rgb(230, 247, 255);
 | 
				
			||||||
 | 
					  padding: 0px 5px;
 | 
				
			||||||
 | 
					  margin-left: -5px;
 | 
				
			||||||
 | 
					  line-height: 18px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-theme-item-flag {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  width: 16px;
 | 
				
			||||||
 | 
					  margin-right: 4px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-icon {
 | 
				
			||||||
 | 
					  width: 18px;
 | 
				
			||||||
 | 
					  height: 18px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.nice-themeselect-menu-item {
 | 
				
			||||||
 | 
					  clear: both;
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  color: rgba(0, 0, 0, 0.65);
 | 
				
			||||||
 | 
					  font-weight: normal;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  line-height: 22px;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-menu-subscribe-more {
 | 
				
			||||||
 | 
					  font-weight: bold;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										128
									
								
								src/component/MenuLeft/Theme.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										128
									
								
								src/component/MenuLeft/Theme.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {RIGHT_SYMBOL, TEMPLATE_NUM, MARKDOWN_THEME_ID, STYLE} from "../../utils/constant";
 | 
				
			||||||
 | 
					import {replaceStyle} from "../../utils/helper";
 | 
				
			||||||
 | 
					import TEMPLATE from "../../template/index";
 | 
				
			||||||
 | 
					import "./Theme.css";
 | 
				
			||||||
 | 
					import axios from "axios";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@inject("view")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Theme extends React.Component {
 | 
				
			||||||
 | 
					  changeTemplate = (item) => {
 | 
				
			||||||
 | 
					    const index = parseInt(item.key, 10);
 | 
				
			||||||
 | 
					    const {themeId, css} = this.props.content.themeList[index];
 | 
				
			||||||
 | 
					    this.props.navbar.setTemplateNum(index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新style编辑器
 | 
				
			||||||
 | 
					    if (themeId === "custom") {
 | 
				
			||||||
 | 
					      this.props.content.setCustomStyle();
 | 
				
			||||||
 | 
					      // 切换自定义自动打开css编辑
 | 
				
			||||||
 | 
					      this.props.view.setStyleEditorOpen(true);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.props.content.setStyle(css);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toggleStyleEditor = () => {
 | 
				
			||||||
 | 
					    const {isStyleEditorOpen} = this.props.view;
 | 
				
			||||||
 | 
					    this.props.view.setStyleEditorOpen(!isStyleEditorOpen);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  subscribeMore = () => {
 | 
				
			||||||
 | 
					    const w = window.open("about:blank");
 | 
				
			||||||
 | 
					    w.location.href = "https://preview.mdnice.com/themes";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentDidMount = async () => {
 | 
				
			||||||
 | 
					    const themeList = [
 | 
				
			||||||
 | 
					      {themeId: "normal", name: "默认主题", css: TEMPLATE.theme.normal},
 | 
				
			||||||
 | 
					      {themeId: "1", name: "橙心", css: TEMPLATE.theme.one},
 | 
				
			||||||
 | 
					      {themeId: "2", name: "姹紫", css: TEMPLATE.theme.two},
 | 
				
			||||||
 | 
					      {themeId: "3", name: "嫩青", css: TEMPLATE.theme.three},
 | 
				
			||||||
 | 
					      {themeId: "4", name: "绿意", css: TEMPLATE.theme.four},
 | 
				
			||||||
 | 
					      {themeId: "5", name: "红绯", css: TEMPLATE.theme.five},
 | 
				
			||||||
 | 
					      {themeId: "6", name: "蓝莹", css: TEMPLATE.theme.six},
 | 
				
			||||||
 | 
					      {themeId: "7", name: "兰青", css: TEMPLATE.theme.seven},
 | 
				
			||||||
 | 
					      {themeId: "8", name: "山吹", css: TEMPLATE.theme.eight},
 | 
				
			||||||
 | 
					      {themeId: "9", name: "网格黑", css: TEMPLATE.theme.wgh},
 | 
				
			||||||
 | 
					      {themeId: "10", name: "极客黑", css: TEMPLATE.theme.ten},
 | 
				
			||||||
 | 
					      {themeId: "11", name: "蔷薇紫", css: TEMPLATE.theme.eleven},
 | 
				
			||||||
 | 
					      {themeId: "12", name: "萌绿风", css: TEMPLATE.theme.twelve},
 | 
				
			||||||
 | 
					      {themeId: "13", name: "全栈蓝", css: TEMPLATE.theme.thirteen},
 | 
				
			||||||
 | 
					      {themeId: "14", name: "极简黑", css: TEMPLATE.theme.fourteen},
 | 
				
			||||||
 | 
					      {themeId: "15", name: "橙蓝风", css: TEMPLATE.theme.fifteen},
 | 
				
			||||||
 | 
					      {themeId: "16", name: "前端之巅同款", css: TEMPLATE.theme.nine},
 | 
				
			||||||
 | 
					      {themeId: "custom", name: "自定义", css: TEMPLATE.theme.custom},
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.props.content.setThemeList(themeList);
 | 
				
			||||||
 | 
					    // 设置一下自定义的规则
 | 
				
			||||||
 | 
					    if (!window.localStorage.getItem(STYLE)) {
 | 
				
			||||||
 | 
					      window.localStorage.setItem(STYLE, TEMPLATE.theme.custom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const templateNum = parseInt(window.localStorage.getItem(TEMPLATE_NUM), 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 主题样式初始化,属于自定义主题则从localstorage中读数据
 | 
				
			||||||
 | 
					    let style = "";
 | 
				
			||||||
 | 
					    if (templateNum === themeList.length - 1) {
 | 
				
			||||||
 | 
					      style = window.localStorage.getItem(STYLE);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (templateNum) {
 | 
				
			||||||
 | 
					        const {css} = themeList[templateNum];
 | 
				
			||||||
 | 
					        style = css;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        style = TEMPLATE.normal;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.props.content.setStyle(style);
 | 
				
			||||||
 | 
					    replaceStyle(MARKDOWN_THEME_ID, style);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {templateNum} = this.props.navbar;
 | 
				
			||||||
 | 
					    const {themeList} = this.props.content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const mdMenu = (
 | 
				
			||||||
 | 
					      <Menu onClick={this.changeTemplate}>
 | 
				
			||||||
 | 
					        {themeList.map((option, index) => (
 | 
				
			||||||
 | 
					          <Menu.Item key={index}>
 | 
				
			||||||
 | 
					            <div id={`nice-menu-theme-${option.themeId}`} className="nice-themeselect-theme-item">
 | 
				
			||||||
 | 
					              <span>
 | 
				
			||||||
 | 
					                <span className="nice-themeselect-theme-item-flag">
 | 
				
			||||||
 | 
					                  {templateNum === index && <span>{RIGHT_SYMBOL}</span>}
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					                <span className="nice-themeselect-theme-item-name">{option.name}</span>
 | 
				
			||||||
 | 
					                {option.isNew && <span className="nice-themeselect-theme-item-new">new</span>}
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </Menu.Item>
 | 
				
			||||||
 | 
					        ))}
 | 
				
			||||||
 | 
					        <Menu.Divider />
 | 
				
			||||||
 | 
					        <li className="nice-themeselect-menu-item">
 | 
				
			||||||
 | 
					          <div id="nice-menu-view-css" className="nice-themeselect-theme-item" onClick={this.toggleStyleEditor}>
 | 
				
			||||||
 | 
					            <span>
 | 
				
			||||||
 | 
					              <span className="nice-themeselect-theme-item-flag">
 | 
				
			||||||
 | 
					                {this.props.view.isStyleEditorOpen && <span>{RIGHT_SYMBOL}</span>}
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					              <span className="nice-themeselect-theme-item-name">查看主题 CSS</span>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </li>
 | 
				
			||||||
 | 
					      </Menu>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={mdMenu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a id="nice-menu-theme" className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          主题
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Theme;
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/component/MenuLeft/View.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										43
									
								
								src/component/MenuLeft/View.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {Menu, Dropdown} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import FullScreen from "./View/FullScreen";
 | 
				
			||||||
 | 
					import EditArea from "./View/EditArea";
 | 
				
			||||||
 | 
					import PreviewArea from "./View/PreviewArea";
 | 
				
			||||||
 | 
					import ThemeArea from "./View/ThemeArea";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const menu = (
 | 
				
			||||||
 | 
					  <Menu>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <FullScreen />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Menu.Divider />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <EditArea />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <PreviewArea />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					    <Menu.Item>
 | 
				
			||||||
 | 
					      <ThemeArea />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					  </Menu>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class View extends Component {
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Dropdown overlay={menu} trigger={["click"]} overlayClassName="nice-overlay">
 | 
				
			||||||
 | 
					        <a id="nice-menu-view" className="nice-menu-link" href="#">
 | 
				
			||||||
 | 
					          查看
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default View;
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/component/MenuLeft/View/EditArea.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										27
									
								
								src/component/MenuLeft/View/EditArea.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {RIGHT_SYMBOL} from "../../../utils/constant";
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("view")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class EditArea extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {isEditAreaOpen} = this.props.view;
 | 
				
			||||||
 | 
					    this.props.view.setEditAreaOpen(!isEditAreaOpen);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-edit-area" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag">{this.props.view.isEditAreaOpen && <span>{RIGHT_SYMBOL}</span>}</span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">编辑区域</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default EditArea;
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/component/MenuLeft/View/FullScreen.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										46
									
								
								src/component/MenuLeft/View/FullScreen.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class FullScreen extends Component {
 | 
				
			||||||
 | 
					  // fullScreen or !fullScreen
 | 
				
			||||||
 | 
					  toggleFullScreen = () => {
 | 
				
			||||||
 | 
					    const doc = window.document;
 | 
				
			||||||
 | 
					    const docEl = doc.documentElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const requestFullScreen =
 | 
				
			||||||
 | 
					      docEl.requestFullscreen ||
 | 
				
			||||||
 | 
					      docEl.mozRequestFullScreen ||
 | 
				
			||||||
 | 
					      docEl.webkitRequestFullScreen ||
 | 
				
			||||||
 | 
					      docEl.msRequestFullscreen;
 | 
				
			||||||
 | 
					    const cancelFullScreen =
 | 
				
			||||||
 | 
					      doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      !doc.fullscreenElement &&
 | 
				
			||||||
 | 
					      !doc.mozFullScreenElement &&
 | 
				
			||||||
 | 
					      !doc.webkitFullscreenElement &&
 | 
				
			||||||
 | 
					      !doc.msFullscreenElement
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      requestFullScreen.call(docEl);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      cancelFullScreen.call(doc);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-full-screen" className="nice-menu-item" onClick={this.toggleFullScreen}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag" />
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">全屏</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default FullScreen;
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/component/MenuLeft/View/PreviewArea.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										27
									
								
								src/component/MenuLeft/View/PreviewArea.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {RIGHT_SYMBOL} from "../../../utils/constant";
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("view")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class PreviewArea extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {isPreviewAreaOpen} = this.props.view;
 | 
				
			||||||
 | 
					    this.props.view.setPreviewAreaOpen(!isPreviewAreaOpen);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-preview-area" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag">{this.props.view.isPreviewAreaOpen && <span>{RIGHT_SYMBOL}</span>}</span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">预览区域</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default PreviewArea;
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/component/MenuLeft/View/ThemeArea.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										27
									
								
								src/component/MenuLeft/View/ThemeArea.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {RIGHT_SYMBOL} from "../../../utils/constant";
 | 
				
			||||||
 | 
					import "../common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("view")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class ThemeArea extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {isStyleEditorOpen} = this.props.view;
 | 
				
			||||||
 | 
					    this.props.view.setStyleEditorOpen(!isStyleEditorOpen);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div id="nice-menu-theme-area" className="nice-menu-item" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-flag">{this.props.view.isStyleEditorOpen && <span>{RIGHT_SYMBOL}</span>}</span>
 | 
				
			||||||
 | 
					          <span className="nice-menu-name">主题CSS区域</span>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ThemeArea;
 | 
				
			||||||
							
								
								
									
										65
									
								
								src/component/MenuLeft/common.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										65
									
								
								src/component/MenuLeft/common.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					.nice-menu-item {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  padding: 5px 12px 5px 6px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-menu-shortcut {
 | 
				
			||||||
 | 
					  color: gray;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-menu-name {
 | 
				
			||||||
 | 
					  margin-right: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-menu-flag {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  width: 16px;
 | 
				
			||||||
 | 
					  margin-right: 4px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-menu-link {
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  padding: 8px 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-menu-link:hover {
 | 
				
			||||||
 | 
					  color: #cecece;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-menu-link:focus {
 | 
				
			||||||
 | 
					  color: #cecece;
 | 
				
			||||||
 | 
					  /*background: #e6f7ff;*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-overlay {
 | 
				
			||||||
 | 
					  top: 56px !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-overlay ul {
 | 
				
			||||||
 | 
					  border-radius: 0px 0px 4px 4px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-overlay ul .ant-dropdown-menu-item {
 | 
				
			||||||
 | 
					  clear: both;
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  color: rgba(0, 0, 0, 0.65);
 | 
				
			||||||
 | 
					  font-weight: normal;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  line-height: 22px;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-overlay ul label {
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.ant-dropdown-menu {
 | 
				
			||||||
 | 
					  padding: 4px !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.ant-dropdown-menu-item:hover, .ant-dropdown-menu-submenu-title:hover {
 | 
				
			||||||
 | 
					  background-color: #e8e8e8 !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										109
									
								
								src/component/SearchBox/SearchBox.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										109
									
								
								src/component/SearchBox/SearchBox.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					.mdnice-searchbox {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  right: 20px;
 | 
				
			||||||
 | 
					  top: 20px;
 | 
				
			||||||
 | 
					  z-index: 20;
 | 
				
			||||||
 | 
					  transition: all 0.15s ease-in-out;
 | 
				
			||||||
 | 
					  padding: 8px;
 | 
				
			||||||
 | 
					  max-width: calc(100% - 40px);
 | 
				
			||||||
 | 
					  background-color: white;
 | 
				
			||||||
 | 
					  height: 72px;
 | 
				
			||||||
 | 
					  box-shadow: 0px 5px 12px rgba(0, 0, 0, 0.05);
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox-replace {
 | 
				
			||||||
 | 
					  margin-top: 8px;
 | 
				
			||||||
 | 
					  margin-left: 27px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox > div {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox div>:not(:last-child ) {
 | 
				
			||||||
 | 
					  margin-right: 3px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox div input {
 | 
				
			||||||
 | 
					  width: 160px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox .searchbox-button {
 | 
				
			||||||
 | 
					  background-color: white;
 | 
				
			||||||
 | 
					  outline: none;
 | 
				
			||||||
 | 
					  padding: 0px;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  height: 24px;
 | 
				
			||||||
 | 
					  width: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox .searchbox-button:active{
 | 
				
			||||||
 | 
					  background-color: #f1f1f1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.searchbox-icon {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  margin: auto;
 | 
				
			||||||
 | 
					  height: 24px;
 | 
				
			||||||
 | 
					  width: 24px;
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.searchbox-icon-casefold {
 | 
				
			||||||
 | 
					  width: 18px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.searchbox-icon-prev {
 | 
				
			||||||
 | 
					  transform: rotate(180deg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.searchbox-icon-replace {
 | 
				
			||||||
 | 
					  width: 16px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.searchbox-icon-fold {
 | 
				
			||||||
 | 
					  transform: rotate(-90deg);
 | 
				
			||||||
 | 
					  transition: all 0.2s ease-in-out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox[data-replace="true"]  .searchbox-icon-fold{
 | 
				
			||||||
 | 
					  transform: unset;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.searchbox-text-highlight {
 | 
				
			||||||
 | 
					  background-color: #91d5ff;
 | 
				
			||||||
 | 
					  line-height: 16px;
 | 
				
			||||||
 | 
					  padding: 3px 0px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* .mdnice-searchbox[data-active="false"] {
 | 
				
			||||||
 | 
					  max-height: 0px !important;
 | 
				
			||||||
 | 
					  padding-top: 0px;
 | 
				
			||||||
 | 
					  padding-bottom: 0px;
 | 
				
			||||||
 | 
					} */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox[data-replace="false"] {
 | 
				
			||||||
 | 
					  height: 40px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox[data-replace="false"] .mdnice-searchbox-replace {
 | 
				
			||||||
 | 
					  visibility: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/* 
 | 
				
			||||||
 | 
					.mdnice-searchbox ~ div .CodeMirror-sizer {
 | 
				
			||||||
 | 
					  transition: all 0.15s ease-in-out;
 | 
				
			||||||
 | 
					} */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox ~ div .CodeMirror-sizer {
 | 
				
			||||||
 | 
					  top: 40px;  
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mdnice-searchbox[data-replace="true"] ~ div .CodeMirror-sizer {
 | 
				
			||||||
 | 
					  top: 72px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										219
									
								
								src/component/SearchBox/index.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										219
									
								
								src/component/SearchBox/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,219 @@
 | 
				
			|||||||
 | 
					/* eslint-disable react/no-did-update-set-state */
 | 
				
			||||||
 | 
					/* eslint-disable react/no-unused-state */
 | 
				
			||||||
 | 
					import React, {createRef} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Input, Tooltip} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./SearchBox.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function WrappedButton(props) {
 | 
				
			||||||
 | 
					  const className = props.className === undefined ? "" : props.className;
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Tooltip placement="bottom" mouseEnterDelay={ENTER_DELAY} mouseLeaveDelay={LEAVE_DELAY} title={props.tipText}>
 | 
				
			||||||
 | 
					      <button className="searchbox-button" type="button" onClick={props.onClick}>
 | 
				
			||||||
 | 
					        <SvgIcon name={props.icon} className={`searchbox-icon ${className}`} fill={props.fill} />
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    </Tooltip>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class SearchBox extends React.Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      replaceText: "",
 | 
				
			||||||
 | 
					      searchText: "",
 | 
				
			||||||
 | 
					      isReplaceOpen: false,
 | 
				
			||||||
 | 
					      cursor: null,
 | 
				
			||||||
 | 
					      caseFold: true,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    this.searchRef = createRef();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** false means next, true means previous */
 | 
				
			||||||
 | 
					  posChange = (direction) => {
 | 
				
			||||||
 | 
					    this.clearMarks();
 | 
				
			||||||
 | 
					    if (typeof direction !== "boolean") {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const {cursor, searchText} = this.state;
 | 
				
			||||||
 | 
					    if (searchText && cursor) {
 | 
				
			||||||
 | 
					      cursor.find(direction);
 | 
				
			||||||
 | 
					      if (cursor.atOccurrence) {
 | 
				
			||||||
 | 
					        this.highlight();
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        while (cursor.find(!direction)) {
 | 
				
			||||||
 | 
					          //  从头开始寻找
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cursor.find(direction);
 | 
				
			||||||
 | 
					        this.highlight();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleCaseFold = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    this.clearMarks();
 | 
				
			||||||
 | 
					    this.setState(
 | 
				
			||||||
 | 
					      (prevState) => {
 | 
				
			||||||
 | 
					        const caseFold = !prevState.caseFold;
 | 
				
			||||||
 | 
					        const cursor = prevState.searchText
 | 
				
			||||||
 | 
					          ? markdownEditor.getSearchCursor(prevState.searchText, null, {caseFold: caseFold})
 | 
				
			||||||
 | 
					          : null;
 | 
				
			||||||
 | 
					        return {caseFold, cursor};
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      () => this.posChange(false),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleScroll = (offset) => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const {top} = markdownEditor.getScrollInfo(offset);
 | 
				
			||||||
 | 
					    console.log(offset);
 | 
				
			||||||
 | 
					    markdownEditor.scrollTo(null, top + offset);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentWillUnmount = () => {
 | 
				
			||||||
 | 
					    this.handleScroll(this.state.isReplaceOpen ? -72 : -40);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentDidMount = () => {
 | 
				
			||||||
 | 
					    this.searchRef.current.focus();
 | 
				
			||||||
 | 
					    this.handleScroll(40);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clearMarks = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    // const markers = markdownEditor.getAllMarks();
 | 
				
			||||||
 | 
					    // markers.forEach((marker) => marker.clear());
 | 
				
			||||||
 | 
					    const cursor = markdownEditor.getCursor();
 | 
				
			||||||
 | 
					    markdownEditor.setSelection(cursor);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  findContent = (value) => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    this.setState(
 | 
				
			||||||
 | 
					      (prevState) => {
 | 
				
			||||||
 | 
					        const cursor = value ? markdownEditor.getSearchCursor(value, null, {caseFold: prevState.caseFold}) : null;
 | 
				
			||||||
 | 
					        return {searchText: value, cursor};
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      () => this.posChange(false),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  highlight = () => {
 | 
				
			||||||
 | 
					    // 高亮前需检测是否有匹配
 | 
				
			||||||
 | 
					    if (this.state.cursor.atOccurrence) {
 | 
				
			||||||
 | 
					      const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					      const from = this.state.cursor.from();
 | 
				
			||||||
 | 
					      const to = this.state.cursor.to();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // markdownEditor.markText(from, to, {
 | 
				
			||||||
 | 
					      //   className: "searchbox-text-highlight",
 | 
				
			||||||
 | 
					      // });
 | 
				
			||||||
 | 
					      markdownEditor.setSelection(from, to);
 | 
				
			||||||
 | 
					      // 防止搜索框挡住文字
 | 
				
			||||||
 | 
					      markdownEditor.scrollIntoView(from, 200);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  replace = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    if (selection) {
 | 
				
			||||||
 | 
					      // 未选中不进行替换
 | 
				
			||||||
 | 
					      markdownEditor.replaceSelection(this.state.replaceText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					      this.props.content.setContent(content);
 | 
				
			||||||
 | 
					      this.posChange(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  replaceAll = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    if (selection && this.state.searchText) {
 | 
				
			||||||
 | 
					      const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					      const searchReg = new RegExp(this.state.searchText, "g");
 | 
				
			||||||
 | 
					      const replaced = content.replace(searchReg, this.state.replaceText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.props.content.setContent(replaced);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handelFoldClick = () => {
 | 
				
			||||||
 | 
					    this.setState((prevState) => {
 | 
				
			||||||
 | 
					      const {isReplaceOpen} = prevState;
 | 
				
			||||||
 | 
					      this.handleScroll(isReplaceOpen ? -32 : 32);
 | 
				
			||||||
 | 
					      return {isReplaceOpen: !isReplaceOpen};
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleClose = () => {
 | 
				
			||||||
 | 
					    this.props.dialog.setSearchOpen(false);
 | 
				
			||||||
 | 
					    this.clearMarks();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const {isReplaceOpen} = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div data-replace={isReplaceOpen} className="mdnice-searchbox">
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					          <WrappedButton icon="down" tipText="展开" onClick={this.handelFoldClick} className="searchbox-icon-fold" />
 | 
				
			||||||
 | 
					          <Input
 | 
				
			||||||
 | 
					            size="small"
 | 
				
			||||||
 | 
					            value={this.state.searchText}
 | 
				
			||||||
 | 
					            placeholder="按Enter进行查找"
 | 
				
			||||||
 | 
					            onChange={(e) => this.findContent(e.target.value)}
 | 
				
			||||||
 | 
					            onPressEnter={() => this.posChange(false)}
 | 
				
			||||||
 | 
					            ref={this.searchRef}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <WrappedButton
 | 
				
			||||||
 | 
					            icon="fontCase"
 | 
				
			||||||
 | 
					            onClick={this.handleCaseFold}
 | 
				
			||||||
 | 
					            tipText="忽略大小写"
 | 
				
			||||||
 | 
					            className="searchbox-icon-casefold"
 | 
				
			||||||
 | 
					            fill={this.state.caseFold ? "red" : undefined}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <WrappedButton
 | 
				
			||||||
 | 
					            icon="down"
 | 
				
			||||||
 | 
					            className="searchbox-icon-prev"
 | 
				
			||||||
 | 
					            onClick={() => this.posChange(true)}
 | 
				
			||||||
 | 
					            tipText="上一个"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <WrappedButton icon="down" onClick={() => this.posChange(false)} tipText="下一个" />
 | 
				
			||||||
 | 
					          <WrappedButton icon="close" onClick={this.handleClose} tipText="关闭" />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div className="mdnice-searchbox-replace">
 | 
				
			||||||
 | 
					          <Input
 | 
				
			||||||
 | 
					            size="small"
 | 
				
			||||||
 | 
					            value={this.state.replaceText}
 | 
				
			||||||
 | 
					            placeholder="按Enter进行替换"
 | 
				
			||||||
 | 
					            onChange={(e) => {
 | 
				
			||||||
 | 
					              this.setState({replaceText: e.target.value});
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            onPressEnter={this.replace}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <WrappedButton icon="replace" className="searchbox-icon-replace" onClick={this.replace} tipText="替换" />
 | 
				
			||||||
 | 
					          <WrappedButton
 | 
				
			||||||
 | 
					            icon="replaceAll"
 | 
				
			||||||
 | 
					            className="searchbox-icon-replace"
 | 
				
			||||||
 | 
					            onClick={this.replaceAll}
 | 
				
			||||||
 | 
					            tipText="替换所有"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SearchBox;
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/component/Sidebar/ExportPdf.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										32
									
								
								src/component/Sidebar/ExportPdf.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import {Tooltip} from "antd";
 | 
				
			||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					import {ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./pdf.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Pdf extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					      window.print();
 | 
				
			||||||
 | 
					    }, 500);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip placement="left" mouseEnterDelay={ENTER_DELAY} mouseLeaveDelay={LEAVE_DELAY} title="导出PDF">
 | 
				
			||||||
 | 
					        <a id="nice-sidebar-pdf" className="nice-btn-pdf" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					          <SvgIcon name="pdf" className="nice-btn-pdf-icon" />
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Pdf;
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/component/Sidebar/Juejin.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/component/Sidebar/Juejin.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					.nice-btn-juejin {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-btn-juejin-icon {
 | 
				
			||||||
 | 
					  padding: 5px 5px 5px 2px;
 | 
				
			||||||
 | 
					  width: 30px;
 | 
				
			||||||
 | 
					  height: 28px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								src/component/Sidebar/Juejin.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										48
									
								
								src/component/Sidebar/Juejin.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {message, Tooltip} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {solveHtml, solveJuejinMath, solveJuejinCode, addJuejinSuffix, copySafari} from "../../utils/converter";
 | 
				
			||||||
 | 
					import {LAYOUT_ID, CODE_NUM, ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					import "./Juejin.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Juejin extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.html = "";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  copyJuejin = () => {
 | 
				
			||||||
 | 
					    if (window.localStorage.getItem(CODE_NUM) === "0") {
 | 
				
			||||||
 | 
					      message.warning("您当前使用的是微信代码主题,请切换其他代码主题后再试!");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const layout = document.getElementById(LAYOUT_ID); // 保护现场
 | 
				
			||||||
 | 
					    const html = layout.innerHTML;
 | 
				
			||||||
 | 
					    solveJuejinMath();
 | 
				
			||||||
 | 
					    addJuejinSuffix();
 | 
				
			||||||
 | 
					    this.html = solveHtml();
 | 
				
			||||||
 | 
					    this.html = solveJuejinCode(this.html);
 | 
				
			||||||
 | 
					    copySafari(this.html);
 | 
				
			||||||
 | 
					    message.success("已复制且添加 mdnice 排版后缀,感谢宣传,请到稀土掘金粘贴");
 | 
				
			||||||
 | 
					    layout.innerHTML = html; // 恢复现场
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip placement="left" mouseEnterDelay={ENTER_DELAY} mouseLeaveDelay={LEAVE_DELAY} title="复制到稀土掘金">
 | 
				
			||||||
 | 
					        <a id="nice-sidebar-juejin" className="nice-btn-juejin" onClick={this.copyJuejin}>
 | 
				
			||||||
 | 
					          <SvgIcon name="juejin" className="nice-btn-juejin-icon" />
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Juejin;
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/component/Sidebar/Markdown.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/component/Sidebar/Markdown.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					.nice-btn-markdwon {
 | 
				
			||||||
 | 
					    padding: 0;
 | 
				
			||||||
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-btn-markdown-icon {
 | 
				
			||||||
 | 
					    padding: 5px 5px 5px 2px;
 | 
				
			||||||
 | 
					    width: 32px;
 | 
				
			||||||
 | 
					    height: 32px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/component/Sidebar/Markdown.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										35
									
								
								src/component/Sidebar/Markdown.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {message, Tooltip} from "antd";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {ENTER_DELAY, LEAVE_DELAY, EXPORT_FILENAME_SUFFIX} from "../../utils/constant";
 | 
				
			||||||
 | 
					import {download, dateFormat} from "../../utils/helper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./Markdown.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class ExportMarkdown extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    if ("download" in document.createElement("a")) {
 | 
				
			||||||
 | 
					      download(content, dateFormat(new Date(), "yyyy-MM-dd") + EXPORT_FILENAME_SUFFIX);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      message.warn("浏览器不支持");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip placement="left" mouseEnterDelay={ENTER_DELAY} mouseLeaveDelay={LEAVE_DELAY} title="导出Markdown">
 | 
				
			||||||
 | 
					        <a id="nice-sidebar-markdown" className="nice-btn-markdown" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					          <SvgIcon name="markdown" className="nice-btn-markdown-icon" />
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ExportMarkdown;
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/component/Sidebar/PreviewType.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/component/Sidebar/PreviewType.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					.nice-btn-previewtype {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-btn-previewtype-icon {
 | 
				
			||||||
 | 
					  padding: 7px;
 | 
				
			||||||
 | 
					  width: 30px;
 | 
				
			||||||
 | 
					  height: 30px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								src/component/Sidebar/PreviewType.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										29
									
								
								src/component/Sidebar/PreviewType.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Tooltip} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					import "./PreviewType.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class PreviewType extends Component {
 | 
				
			||||||
 | 
					  handleClick = (key) => {
 | 
				
			||||||
 | 
					    this.props.navbar.setPreviewType(key);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const targetType = this.props.navbar.previewType === "pc" ? "mobile" : "pc";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip placement="left" mouseEnterDelay={ENTER_DELAY} mouseLeaveDelay={LEAVE_DELAY} title="预览模式">
 | 
				
			||||||
 | 
					        <a id="nice-sidebar-preview-type" className="nice-btn-previewtype" onClick={() => this.handleClick(targetType)}>
 | 
				
			||||||
 | 
					          <SvgIcon name={targetType} className="nice-btn-previewtype-icon" />
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default PreviewType;
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/component/Sidebar/Wechat.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/component/Sidebar/Wechat.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					.nice-btn-wechat {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-btn-wechat-icon {
 | 
				
			||||||
 | 
					  padding: 6px;
 | 
				
			||||||
 | 
					  width: 30px;
 | 
				
			||||||
 | 
					  height: 30px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/component/Sidebar/Wechat.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										42
									
								
								src/component/Sidebar/Wechat.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {message, Tooltip} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {solveWeChatMath, solveHtml, copySafari} from "../../utils/converter";
 | 
				
			||||||
 | 
					import {LAYOUT_ID, ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					import "./Wechat.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Wechat extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.html = "";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  copyWechat = () => {
 | 
				
			||||||
 | 
					    const layout = document.getElementById(LAYOUT_ID); // 保护现场
 | 
				
			||||||
 | 
					    const html = layout.innerHTML;
 | 
				
			||||||
 | 
					    solveWeChatMath();
 | 
				
			||||||
 | 
					    this.html = solveHtml();
 | 
				
			||||||
 | 
					    copySafari(this.html);
 | 
				
			||||||
 | 
					    message.success("已复制,请到微信公众平台粘贴");
 | 
				
			||||||
 | 
					    layout.innerHTML = html; // 恢复现场
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip placement="left" mouseEnterDelay={ENTER_DELAY} mouseLeaveDelay={LEAVE_DELAY} title="复制到公众号">
 | 
				
			||||||
 | 
					        <a id="nice-sidebar-wechat" className="nice-btn-wechat" onClick={this.copyWechat}>
 | 
				
			||||||
 | 
					          <SvgIcon name="wechat" className="nice-btn-wechat-icon" />
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Wechat;
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/component/Sidebar/Zhihu.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/component/Sidebar/Zhihu.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					.nice-btn-zhihu {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-btn-zhihu-icon {
 | 
				
			||||||
 | 
					  padding: 5px;
 | 
				
			||||||
 | 
					  width: 30px;
 | 
				
			||||||
 | 
					  height: 30px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/component/Sidebar/Zhihu.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										46
									
								
								src/component/Sidebar/Zhihu.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {message, Tooltip} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {solveHtml, solveZhihuMath, copySafari} from "../../utils/converter";
 | 
				
			||||||
 | 
					import {LAYOUT_ID, CODE_NUM, ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					import "./Zhihu.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@inject("navbar")
 | 
				
			||||||
 | 
					@inject("imageHosting")
 | 
				
			||||||
 | 
					@inject("dialog")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Zhihu extends Component {
 | 
				
			||||||
 | 
					  constructor(props) {
 | 
				
			||||||
 | 
					    super(props);
 | 
				
			||||||
 | 
					    this.html = "";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  copyZhihu = () => {
 | 
				
			||||||
 | 
					    if (window.localStorage.getItem(CODE_NUM) === "0") {
 | 
				
			||||||
 | 
					      message.warning("您当前使用的是微信代码主题,请切换其他代码主题后再试!");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const layout = document.getElementById(LAYOUT_ID); // 保护现场
 | 
				
			||||||
 | 
					    const html = layout.innerHTML;
 | 
				
			||||||
 | 
					    solveZhihuMath();
 | 
				
			||||||
 | 
					    this.html = solveHtml();
 | 
				
			||||||
 | 
					    copySafari(this.html);
 | 
				
			||||||
 | 
					    message.success("已复制,请到知乎粘贴");
 | 
				
			||||||
 | 
					    layout.innerHTML = html; // 恢复现场
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip placement="left" mouseEnterDelay={ENTER_DELAY} mouseLeaveDelay={LEAVE_DELAY} title="复制到知乎">
 | 
				
			||||||
 | 
					        <a id="nice-sidebar-zhihu" className="nice-btn-zhihu" onClick={this.copyZhihu}>
 | 
				
			||||||
 | 
					          <SvgIcon name="zhihu" className="nice-btn-zhihu-icon" />
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Zhihu;
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/component/Sidebar/pdf.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								src/component/Sidebar/pdf.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					.nice-btn-pdf {
 | 
				
			||||||
 | 
					    padding: 0;
 | 
				
			||||||
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nice-btn-pdf-icon {
 | 
				
			||||||
 | 
					    padding: 6px;
 | 
				
			||||||
 | 
					    width: 30px;
 | 
				
			||||||
 | 
					    height: 30px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/component/Toolbar/Bold.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										42
									
								
								src/component/Toolbar/Bold.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {Tooltip} from "antd";
 | 
				
			||||||
 | 
					import {ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					import {bold} from "../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../utils/hotkey";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Bold extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    bold(markdownEditor, selection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip
 | 
				
			||||||
 | 
					        placement="bottom"
 | 
				
			||||||
 | 
					        mouseEnterDelay={ENTER_DELAY}
 | 
				
			||||||
 | 
					        mouseLeaveDelay={LEAVE_DELAY}
 | 
				
			||||||
 | 
					        title={"快捷键:" + hotKeys.bold}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <a id="nice-sidebar-bold" className="nice-btn-tool" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					          <SvgIcon name="bold" className="nice-btn-tool-icon" />
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Bold;
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/component/Toolbar/Code.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										42
									
								
								src/component/Toolbar/Code.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import React, {Component} from "react";
 | 
				
			||||||
 | 
					import {observer, inject} from "mobx-react";
 | 
				
			||||||
 | 
					import {ENTER_DELAY, LEAVE_DELAY} from "../../utils/constant";
 | 
				
			||||||
 | 
					import {Tooltip} from "antd";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {code} from "../../utils/editorKeyEvents";
 | 
				
			||||||
 | 
					import {hotKeys} from "../../utils/hotkey";
 | 
				
			||||||
 | 
					import SvgIcon from "../../icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./common.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject("content")
 | 
				
			||||||
 | 
					@observer
 | 
				
			||||||
 | 
					class Code extends Component {
 | 
				
			||||||
 | 
					  handleClick = () => {
 | 
				
			||||||
 | 
					    const {markdownEditor} = this.props.content;
 | 
				
			||||||
 | 
					    const selection = markdownEditor.getSelection();
 | 
				
			||||||
 | 
					    code(markdownEditor, selection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 上传后实时更新内容
 | 
				
			||||||
 | 
					    const content = markdownEditor.getValue();
 | 
				
			||||||
 | 
					    this.props.content.setContent(content);
 | 
				
			||||||
 | 
					    markdownEditor.focus();
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Tooltip
 | 
				
			||||||
 | 
					        placement="bottom"
 | 
				
			||||||
 | 
					        mouseEnterDelay={ENTER_DELAY}
 | 
				
			||||||
 | 
					        mouseLeaveDelay={LEAVE_DELAY}
 | 
				
			||||||
 | 
					        title={"快捷键:" + hotKeys.code}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <a id="nice-sidebar-code" className="nice-btn-tool" onClick={this.handleClick}>
 | 
				
			||||||
 | 
					          <SvgIcon name="code" className="nice-btn-tool-icon" />
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </Tooltip>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Code;
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user