refactor: remove docs in favor of relocation to standardnotes.com/help

This commit is contained in:
moughxyz
2023-01-18 07:04:36 -06:00
parent 24cda40dde
commit 76937ba68c
1024 changed files with 219 additions and 102585 deletions

View File

@@ -1,20 +0,0 @@
# Dependencies
node_modules
# Production
build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -1,3 +0,0 @@
{
"singleQuote": true
}

View File

@@ -1,72 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.2.9](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.8...@standardnotes/docs@0.2.9) (2023-01-16)
**Note:** Version bump only for package @standardnotes/docs
## [0.2.8](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.7...@standardnotes/docs@0.2.8) (2023-01-10)
**Note:** Version bump only for package @standardnotes/docs
## [0.2.7](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.6...@standardnotes/docs@0.2.7) (2023-01-10)
**Note:** Version bump only for package @standardnotes/docs
## [0.2.6](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.5...@standardnotes/docs@0.2.6) (2023-01-03)
**Note:** Version bump only for package @standardnotes/docs
## [0.2.5](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.4...@standardnotes/docs@0.2.5) (2022-12-28)
### Bug Fixes
* **docs:** update self-hosted instructions ([1c48dd1](https://github.com/standardnotes/app/commit/1c48dd135ced93832495fdd89b2940635f04205a))
## [0.2.4](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.3...@standardnotes/docs@0.2.4) (2022-12-19)
**Note:** Version bump only for package @standardnotes/docs
## [0.2.3](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.2...@standardnotes/docs@0.2.3) (2022-12-07)
**Note:** Version bump only for package @standardnotes/docs
## [0.2.2](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.1...@standardnotes/docs@0.2.2) (2022-11-15)
**Note:** Version bump only for package @standardnotes/docs
## [0.2.1](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.2.0...@standardnotes/docs@0.2.1) (2022-10-05)
### Bug Fixes
* **docs:** Change order of sed commands ([#1742](https://github.com/standardnotes/app/issues/1742)) ([de0f91c](https://github.com/standardnotes/app/commit/de0f91c9e38883613f942ba85d9f3d0bc360dba3))
# [0.2.0](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.1.0...@standardnotes/docs@0.2.0) (2022-08-17)
### Features
* **docs:** remove matomo in favour of plausible ([6d7bcd7](https://github.com/standardnotes/app/commit/6d7bcd7a8d2dfffb6dc69899406af57e41c071df))
# [0.1.0](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.0.3...@standardnotes/docs@0.1.0) (2022-07-11)
### Features
* **docs:** improve docker page ([#1240](https://github.com/standardnotes/app/issues/1240)) ([b025e39](https://github.com/standardnotes/app/commit/b025e394c129a53f41cb5835943901cf40fc92b6))
## [0.0.3](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.0.2...@standardnotes/docs@0.0.3) (2022-07-09)
### Bug Fixes
* **docs:** improve getting started self host page ([#1238](https://github.com/standardnotes/app/issues/1238)) ([4ad21c4](https://github.com/standardnotes/app/commit/4ad21c4f61e26dfe3284c5def1baebb7a497d200))
## [0.0.2](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.0.1...@standardnotes/docs@0.0.2) (2022-07-05)
**Note:** Version bump only for package @standardnotes/docs
## [0.0.1](https://github.com/standardnotes/app/compare/@standardnotes/docs@0.1.0...@standardnotes/docs@0.0.1) (2022-07-04)
### Bug Fixes
* docs build ([#1203](https://github.com/standardnotes/app/issues/1203)) ([66d5dce](https://github.com/standardnotes/app/commit/66d5dce5e17674e70b24a6d780afde5cfb863715))

View File

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

View File

@@ -1,195 +0,0 @@
# Standard Notes Documentation
## Introduction
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. You can read the about the full features of Docusaurus on the official [Docusaurus documentation](https://docsaurus.io).
Docusaurus is a static site generator, which means that the production version of the website contains only static assets such as HTML, CSS, and JavaScript, and the entire website is rendered before it is loaded onto the production server. The website can then be served on static hosting service such as GitHub Pages.
Each push to the `main` branch of the repository will automatically deploy the website to GitHub Pages. The configuration for this continuous deployment is located at [./.github/workflows/deploy.yml](./.github/workflows/deploy.yml). The URL for the website is located at [static/CNAME](static/CNAME).
### Directory
This is the structure of the directory:
```sh
docs
├── docs
│ ├── account
│ ├── extended
│ ├── extensions
│ ├── listed
│ ├── privacy
│ ├── self-hosting
│ ├── specification
│ ├── troubleshooting
│ ├── usage
│ ├── contribute.md
│ ├── template.md
│ └── welcome.md
├── src
│ ├── components
│ │ └── CanonicalUrl.js
│ ├── css
│ │ └── custom.scss
│ └── pages
│ ├── archive
│ ├── docs
│ ├── listed
│ ├── self-hosting
│ ├── standard-file
│ └── styles.module.css
├── static
│ ├── img
│ ├── katex
│ ├── .nojekyll
│ ├── CNAME
├── .gitignore
├── .prettierrc
├── docusaurus.config.js
├── LICENSE
├── package.json
├── README.md
├── sidebars.js
└── yarn.lock
```
### Files
- `.gitignore` - This specifies which files Git should ignore when committing and pushing to remote repositories.
- `.prettierrc` - This specifies the rules for [Prettier](https://prettier.io/docs/en/configuration.html), a code formattter
- `docusaurus.config.js` - This is the configuration file for Docusaurus. You can manage the links in the header and footer, and site metadata here. A more in-depth introduction to this configuration file is available on the [Docusaurus website](https://docusaurus.io/docs/configuration) and full documentation for the API is [here](https://docusaurus.io/docs/docusaurus.config.js).
- `package.json` - This specifies the dependencies for the website and the commands that you can run with yarn.
- `sidebars.js` - This specifies the sidebars for your documentation. The full documentation for this file is available on the [Docusaurus website](https://docusaurus.io/docs/sidebar).
- `yarn.lock` - This specifies the full dependencies for the website including the dependencies of the dependencies listed in `package.json`. Do not edit this file directly. Edit `package.json` instead, then run `yarn install` as described below.
The `.md` files in the `docs` directory are the docs. See the [Docusaurus website](https://docusaurus.io/docs/docs-introduction) for the full documentation on how to create docs and to manage the metadata.
The `custom.scss` file in `src/css` defines global styles as described on the [Docusaurus website](https://docusaurus.io/docs/styling-layout/).
The `.js` files in `src/pages` are [Docusaurus pages](https://docusaurus.io/docs/creating-pages) that serve as redirects. You can also set up redirects using the [plugin-client-redirects](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-client-redirects) plugin in `docusaurus.config.js`
## Development
A full list of the commands for the Docusaurus CLI is available at the [Docusaurus website](https://docusaurus.io/docs/cli).
### Prerequisites
To get started with developing this website, you need to install the following technologies on your device
- [Node.js](https://nodejs.org/en/download/) version >= 12.13.0 or above (which can be checked by running `node -v`). You can use [nvm](https://github.com/nvm-sh/nvm) for managing multiple Node versions on a single machine installed
- [Yarn](https://yarnpkg.com/en/) version >= 1.5 (which can be checked by running `yarn --version`). Yarn is a performant package manager for JavaScript and replaces the `npm` client. It is not strictly necessary, but highly encouraged because we use it.
We also recommend that you download the following technologies:
- [Visual Studio Code](https://code.visualstudio.com/) - A text editor to edit the source files of your editor
- [Git](https://git-scm.com/downloads) - A tool for managing different versions of the website
### Installation
```console
yarn install
```
This command reads the dependencies from the `package.json` file, updates the dependencies of those dependencies in
### Start
```console
yarn start
```
This command starts a local development server at port `3002`, opens up a browser window, and loads [localhost:3002](localhost:3002). Most changes are reflected live without having to restart the server. To close this, press <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>C</kbd>.
### Build
```console
yarn build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
### Serve
```console
yarn serve
```
Serve your built website locally through port `3000`. Open [localhost:3000](localhost:3000) to test the production build of your website before committing it to Git.
### Deployment
```console
GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy
```
We use GitHub Pages to host our website. This command is a convenient way to build the website and push to the `gh-pages` branch.
To use this command, you need to [set up SSH](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh) and make sure that your `git remote` URL uses SSH. To check your `git remote`, run `git remote -v`. The console should output something like this:
```console
origin git@github.com:standardnotes/docs.git (fetch)
origin git@github.com:standardnotes/docs.git (push)
```
If the part after `origin` begins with `https://` instead of `git@`, run this:
```console
git remote set-url origin git@github.com:standardnotes/docs.git
```
## Creating a new documentation file
1. Try to name the new documentation file using the desired URL path for that file. The default `id` is the name of the file before the `.md`, so naming the documentation files this way allows linking between docs with path names (e.g., `./self-hosting/getting-started.md`).
2. Copy the contents of `docs/template` into your new documentation file. The template contains standard keywords and a link to the standard meta image.
3. In VSCode, you can easily change `Template` to be desired word or phrase by right-clicking the word `Template` then clicking "Change all occurrences".
4. Finish writing the new documentation.
5. Add the `id` to `sidebars.js`.
6. Run `yarn build` before committing and make sure that the build process is successful.
When possible, use path names when linking between docs (see #1 above). This improves the editing experience because we can easily switch between docs in VSCode by pressing <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> and clicking the links. Docusaurus will automatically remove the `.md` in the path of the doc. If you are developing and the `.md` does not update immediately, shut down your development server and restart it.
## Style Guide
Please follow the following writing style guide. These guidelines are open for discussion and are subject to change as we see fit, but we should be consistent about them:
- Add periods to complete sentences in lists.
- Use "sign in to" instead of "sign into" to be consistent with the [web app](https://app.standardnotes.com).
- Hyphenate `open-source` when using the phrase as an adjective (see [Merriam-Webster's](https://www.merriam-webster.com/dictionary/open-source)).
- The _-n't_ contractions are fine, but try to avoid using noun-verb contractions, such as ("you're" or "you've"). (See [Google's convention](https://developers.google.com/style/contractions)).
For more inspiration for the style guide, see the [Google's developer documentation style guide](https://developers.google.com/style) or [Microsoft's Writing Style Guide](https://docs.microsoft.com/en-us/style-guide/welcome/).
## Configuring Search
The search is powered by [Algolia DocSearch](https://docsearch.algolia.com/), a free service. Thank you, Algolia!
About every 24 hours, Algolia scrapes the website and updates the search index. You do not need to do anything to re-scrape the website and update the search index.
You can modify which content appears in the search index by updating the DocSearch configuration file. The DocSearch configuration file for Standard Notes is available at [algolia/docsearch-configs/../standardnotes.json](https://github.com/algolia/docsearch-configs/blob/master/configs/standardnotes.json). Try to keep it up to date with [algolia/docsearch-configs/../docusaurus-2.json](https://github.com/algolia/docsearch-configs/blob/master/configs/docusaurus-2.json).
## Updating KaTeX
The version of KaTeX that we can use depends on the [Remark Math](https://github.com/remarkjs/remark-math) that we use. The version of Remark Math that we can use depends on [Remark Parse](https://github.com/remarkjs/remark/releases) that we use. As of May 7, 2021, Docusaurus does not support the latest version of Remark Parse, so it does not support the latest version of Remark Math and KaTeX. You can follow the developments in [Docusaurus #3668](https://github.com/facebook/docusaurus/issues/3668) and [Docusaurus #4029](https://github.com/facebook/docusaurus/issues/4029).
To update KaTeX:
1. Determine which version of KaTeX you want to use (e.g., `v0.12.0`).
2. Create a folder in [./static/katex](./static/katex) with the version number as the name of the folder.
3. Download the zip of the appropriate version from [github.com/katex/katex/releases](https://github.com/KaTeX/KaTeX/releases).
4. Unzip the KaTeX from step 2 and copy the contents into the folder from step 1.
5. Copy the `integrity` of the `katex.min.css` file from the `README.md` of the unzipped contents from step 3.
6. Update the `href` and `integrity` of the KaTeX stylesheet in `docusaurus.config.js`.
7. Check the page for Markdown Math to see if KaTeX is loading properly.
## Updating Canonical URLs
You can update the canonical URL of individual docs by importing the `CanonicalUrl` component defined in [./src/components/CanonicalUrl.js](./src/components/CanonicalUrl.js).
Update the relative path as appropriate. To check that the canonical is updated, build the website using `yarn build`, and test the site using `yarn serve`.
```jsx
import CanonicalUrl from '../../src/components/CanonicalUrl';
<CanonicalUrl canonicalUrl="https://standardnotes.com/help/" />;
```

View File

@@ -1,3 +0,0 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
}

View File

@@ -1,21 +0,0 @@
---
title: How to contribute
sidebar: How to contribute
description: How to contribute to Standard Notes
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Thank you for your interest in contributing to Standard Notes. We are very happy that you want to help. The development for all our apps are done publicly on [GitHub](https://github.com/standardnotes). Here are some ways that you can help us improve Standard Notes:
1. Submit suggestions and feature ideas to our [community forum](https://standardnotes.com/forum). You can comment on the existing feature requests and subscribe to individual threads to follow their progress.
2. Report bugs or issues to our [community forum](https://standardnotes.com/forum).
3. Help others on the forum and our [Discord group](https://standardnotes.com/discord).
4. Blog, tweet and share what you like about Standard Notes, what you use it for, and how it helps you.

View File

@@ -1,191 +0,0 @@
---
id: actions
title: Actions
sidebar_label: Actions
description: How to build Action components for Standard Notes.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- actions
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Actions are menu-based extensions that allow you to build simple APIs that do not require a user interface. Actions have the power to receive the working note and modify it. We use actions for our Note History extension, as well as Listed and File Attachments.
## Building an Action
Building an action-based extension can be done through any backend system of your choosing. We use Rails to build the [Listed](https://github.com/standardnotes/listed) extension, which allows you to create and manage a blogging publication from your notes.
In this example, we'll recreate a simple clone of Listed.
1. Generate a secret installation link for the user.
The secret installation link will contain a randomly generated secret key that authenticates the user to the server. The user need only copy the resulting link into Standard Notes, and it is then remembered automatically and sent to the server with every subsequent request.
```ruby
author = Author.new
secret = Digest::SHA256.hexdigest(SecureRandom.hex)
secret_url = CGI.escape("#{ENV['HOST']}/authors/#{author.id}/extension/?secret=#{secret}&type=sn")
```
Display the `secret_url` to the user and instruct them to install the url in Standard Notes, via the Extensions menu in the lower left corner.
1. Whenever the user clicks on the "Actions" menu within Standard Notes, SN will make a GET request to the user's secret URL. It is here that you return a JSON object that contains some metadata and applicable actions.
Here is what Listed handles the `extension` endpoint that is encoded in the user's secret url:
```ruby
def extension
secret = params[:secret]
item_uuid = params[:item_uuid]
name = "Listed"
supported_types = ["Note"]
actions = []
if item_uuid
actions += [
{
:label => "Publish to Blog",
:url => "#{ENV['HOST']}/authors/#{@author.id}/posts/?unlisted=false&secret=#{secret}&item_uuid=#{item_uuid}",
:verb => "post",
:context => "Item",
:content_types => ["Note"],
:access_type => "decrypted"
},
{
:label => "Open Blog",
:url => @author.username && @author.username.length > 0 ? @author.url : "#{ENV['HOST']}/authors/#{@author.id}",
:verb => "show",
:context => "Item",
:content_types => ["Note"],
:access_type => "decrypted"
},
{
:label => "Publish to Private Link",
:url => "#{ENV['HOST']}/authors/#{@author.id}/posts/?unlisted=true&secret=#{secret}&item_uuid=#{item_uuid}",
:verb => "post",
:context => "Item",
:content_types => ["Note"],
:access_type => "decrypted"
}
]
end
post = Post.find_by_item_uuid(item_uuid)
if post
actions.push(
{
:label => "Open Private Link",
:url => "#{ENV['HOST']}/#{post.token}",
:verb => "show",
:context => "Item",
:content_types => ["Note"]
})
actions.push(
{
:label => "Unpublish",
:url => "#{ENV['HOST']}/authors/#{@author.id}/posts/#{post.id}/unpublish?secret=#{secret}",
:verb => "post",
:context => "Item",
:content_types => ["Note"]
})
end
actions.push (
{
:label => "Settings",
:url => "#{ENV['HOST']}/authors/#{@author.id}/settings?secret=#{secret}",
:verb => "show",
:context => "Item",
:content_types => ["Note"]
}
)
description = "Publishes to listed.to. Requires decrypted access to publishing note."
render :json => {
:name => name,
:description => description,
:supported_types => supported_types,
:actions => actions,
:content_type => "Extension",
:identifier => "com.my.extension"
}
end
```
1. When a user selects the action, your server should be ready to handle that endpoint, and in most cases expect an item. Here's how Listed handles the "Publish to Blog" action:
```ruby
def create
item_uuid = params[:item_uuid]
post = Post.find_by_item_uuid(item_uuid)
if post && post.author != @author
return
end
if !post
post = @author.posts.new(post_params)
else
post.update(post_params)
end
item = params[:items][0]
content = item["content"]
post.title = content["title"]
post.text = content["text"]
post.save
end
```
### Properties
Actions have the following properties:
| Key | Description |
| :------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`label`** | What the UI will display for this action. |
| **`url`** | The URL that Standard Notes will make a request to when the user selects this action. |
| **`verb`** | Instructs Standard Notes how to handle the URL. This can be one of: |
| **`show`** | Standard Notes will open the `url` in a browser. |
| **`post`** | Standard Notes will make a POST request to the `url` with the current item included in the parameters. |
| **`get`** | Standard Notes will make a GET request to the `url` and expect an `Item` in response. The item will be used to update the current working note. We use this for our Note History extension to update the current note with a previous version of it. |
| **`render`** | Standard Notes will make a `GET` request to the `url` and expect an `Item`, but instead of updating the item, it will preview it in a modal. This allows a user to preview the contents of an incoming item before choosing to replace the current note with whatever is retrieved from the server. We also use this in our Note History extension. |
| **`context`** | Context should mostly be `Item`, which means that this action applies to a particular item, and is not just a general action. In the past, `context` could take on the value of `global`, which means it has actions available that are not related to an item. However, this functionality is unofficially deprecated, with an official deprecation coming soon. |
| **`content_types`** | The kinds of items this action applies to. Currently only 'Note' actions are supported. In the future, we might allow for actions on a `Tag` or other content types, but no such interface is currently available. |
For example, the expected response of a **`get`** action is:
```json
{
"item": {
"uuid": "",
"content_type": "",
"content": "",
"created_at": "",
"updated_at": ""
}
}
```
The payload inside the `item` key is the same payload structure you would see if you downloaded an encrypted backup file from the Account menu and inspected the `.txt` file. The item needs to be in the encrypted format when it appears. We'll need to modify the client code to also accept decrypted items.
## Installing an Action
Actions have the following URL format:
```
https://host.org/my-action?type=action&name=MyAction
```
## Action Potential
There are a lot of cool things you can build with actions. For example, you can build an action that receives the current note which consists of a bunch of numbers separated by a comma, and the action can compute the average, and return the new note contents which appends the average. This is a simple use case, but can be enlarged to build more powerful abilities.
You might even build an action that for example receives JavaScript code in the note text, runs the JavaScript, computes the result, and returns the result which is then appended to the note body in creative ways. The possibilities are almost endless.

View File

@@ -1,143 +0,0 @@
---
id: building-an-extension
title: Building an Extension
sidebar_label: Building an Extension
description: How to build an extension for Standard Notes.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
In this section, we'll talk about building **components**, which are a type of extension related to user interface elements that can replace or be appended to areas of the Standard Notes app. They allow us to do cool things like custom editors, nested folders, tag autocomplete, and custom extension bars in the editor pane.
Building a component is easily done using the JavaScript-based [Component Bridge library](https://github.com/sn-extensions/components-api). All you have to do is build a single-page web app using any framework you'd like (plain, Angular, React, etc.), then use our component "bridge" to handle interactions between Standard Notes and your extension, for example, to read or save data.
## Setting up the project
In this example, we'll use our blank-slate ReactJS template to build a utility bar that counts and displays the number of words in the current note.
(The ReactJS template makes it easy to get started. You can also create a project from scratch that utilizes the [Components Bridge library](https://github.com/sn-extensions/components-api).)
1. Clone the [blank-slate](https://github.com/sn-extensions/react-blank-slate) project from GitHub:
```bash
git clone https://github.com/sn-extensions/react-blank-slate.git
```
1. Build the project:
```bash
cd react-blank-slate
npm install
```
1. Start the local web server to host the app.
```bash
npm run start
```
1. In the command output from above, note the port number used. By default, it will probably be port 8080 if it's available. Open `localhost:8080` in your browser. You should see the text "Component is ready" on the page.
## Installing in Standard Notes
1. In the `app` folder, you will find a file called `ext.json`. This file instructs Standard Notes on how to install your extension. After having run the last step from the previous section (`npm run start`), you should have a localhost endpoint running.
1. In your browser, open `http://localhost:8080/ext.json`, and ensure that the output matches the file contents of the ext.json file. Most importantly, if your dev server is running on a different port, make sure to update the `url` property of the JSON file to reflect the correct value.
1. In the **Standard Notes** desktop application (browser may not work for this), click **Extensions** in the lower left corner of the app, click **Import Extension** in the bottom right of the **Extensions** window, and input your ext.json location: `http://localhost:8080/ext.json`. Then press enter.
1. In the same window, find your installed extension, then press **Activate** to run it.
1. You should now see "Blank Slate" at the bottom left of your notes. Clicking on it should raise a little window which displays "Your component is ready". If you try in your browser, it is possible that the window instead remains white. This is most likely due to your browser blocking Mixed Content page. Search online for how to enable it for your browser.
More detailed instructions on setting up your local environment can be found in the [Local Setup tutorial](/extensions/local-setup/).
## Writing the App
1. In order to count the number of words in a note, the component needs access to the "working note", or the note the user is currently editing. In `app/lib/BridgeManager.js`, uncomment the relevant parts of the permissions so it looks like this:
```javascript
var permissions = [
{
name: 'stream-context-item',
},
];
```
1. Uncomment the function `streamContextItem` so it looks like this:
```javascript
this.componentManager.streamContextItem((item) => {
this.note = item;
this.notifyObserversOfUpdate();
});
```
Whenever a change is made to the working note, the block in that function will be called automatically.
1. In `app/components/Home.js`, create a function called `analyzeNote` that will count the number of words in the note's text:
```javascript
analyzeNote() {
let wordCount = this.state.note.content.text.match(/\b/gm).length / 2;
this.setState({wordCount: wordCount});
}
```
1. In the constructor of the Home class, call `analyzeNote` in the BridgeManager updateObserver so that it looks like this:
```javascript
BridgeManager.get().addUpdateObserver(() => {
this.setState({ note: BridgeManager.get().getNote() });
this.analyzeNote();
});
```
1. In the `render` function, add the following inside the first `div` of the `{this.state.note}` conditional:
```html
<p>Number of words: <strong>{this.state.wordCount}</strong></p>
```
Save all changes, then reload the entire Standard Notes web page. You should now see your word count update live as you type.
**Important:** The dev server auto-reloads the extension window inside Standard Notes, and by doing so, destroys the bridge connection between Standard Notes and the extension. Whenever you make a change, it's best to reload the entire Standard Notes window via Ctrl/Cmd + R in either the web or desktop app.
---
If you'd like to see the finished product, switch to the `word-count` branch:
```bash
git checkout word-count
```
## Available Areas
Areas tell Standard Notes where to display a particular component. The current list of available areas are:
| Key | Description |
| :-------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tags-list` | replaces the tags pane with a custom component. We use this for the Folders component. |
| `note-tags` | replaces the editor pane's tags area with a custom component. We use this for autocomplete tags. |
| `editor-stack` | adds custom-sized components in a stack in the editor pane. This does not replace any native modules but simply adds layers on top of the editor pane. We use this for the Action Bar and GitHub Push components. |
| `editor-editor` | replaces the plain text editor with a custom editor. We use this for all of our editors, including Markdown, Code, and Plus. |
| `themes` | replaces the default css styles with a custom set of styles. |
![Image of areas](/img/extensions/areas.png)
## Next Steps
There are an unlimited number of things you can build with components that do anything from nested folders in the tags pane and autocomplete in the editor pane, to pushing notes to GitHub or WordPress.
To see how we built [our own components](https://standardnotes.com/features), study the source code [available here](https://github.com/standardnotes/plugins).
For questions on development, [post in the forum](https://standardnotes.com/forum) or [join our Discord](https://standardnotes.com/discord).
If you'd like to support Standard Notes and use our secure hosting to install all the components we have to offer, consider purchasing the [Extended subscription](https://standardnotes.com/plans).

View File

@@ -1,38 +0,0 @@
---
title: How to build editors with EditorKit
sidebar_label: EditorKit
description: How to use the Standard Notes EditorKit to build an editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- EditorKit
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
The EditorKit is a wrapper for the Components API to make it easier to develop editors for Standard Notes.
## Examples
The documentation for the EditorKit is incomplete. You can view the following examples to see how it is used.
- [Token Vault](https://github.com/sn-extensions/token-vault)
## Development
The Standard Notes EditorKit is written in JavaScript and compiled with Webpack.
## License
The Standard Notes EditorKit is licensed under the GNU [AGPL-3.0-or-later](https://github.com/standardnotes/editor-kit/blob/master/LICENSE).
## Resources
- [GitHub](https://github.com/standardnotes/editor-kit)
- [NPM](https://www.npmjs.com/package/sn-editor-kit)

View File

@@ -1,98 +0,0 @@
---
slug: editors/getting-started
id: editors-getting-started
title: Getting Started with Building Editors
sidebar_label: Getting Started
description: Getting Started with Building Editors
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Getting Started with Building Editors
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Overview
This page provides a list of links to the technologies and practices that we recommend using for building editors. These technologies are used in the [Editor Template - Create React App and TypeScript](https://github.com/standardnotes/editor-template-cra-typescript). If you are familiar with React, TypeScript, and Sass, then you can skip this overview and go straight to the [README.md of the Editor Template](https://github.com/standardnotes/editor-template-cra-typescript#readme).
### Platforms
- [GitHub](https://github.com/) - A website to store the source code of your editor and to host a usable copy of your editor
### Programming Languages
We recommend using the following programming languages to build editors:
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML) - A programming language for conveying meaning
- [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS) - A programming language for conveying style
- [SCSS](https://sass-lang.com/documentation/syntax) - A programming language that is like CSS but is easier to write
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) - A programming language for performing tasks
- [TypeScript](https://www.typescriptlang.org/) - A programming language that is like JavaScript but is easier to check for errors
### Environment
To get started with building editors, we recommend downloading the following technologies on your desktop computer:
- [Node.js](https://nodejs.org/) - An environment where you can run JavaScript code to run
- NPM or [Yarn](https://yarnpkg.com/) - A [package manager](https://wikipedia.org/wiki/Package_manager) for the JavaScript programming language. NPM comes with Node.js, but we use Yarn
- [Visual Studio Code](https://code.visualstudio.com/) - A text editor to edit the source files of your editor
- [Git Bash](https://git-scm.com/downloads) - A tool to interact with your file system
### Packages and Libraries
We recommend using the following packages and libraries:
- [Prettier](https://prettier.io/docs/en/index.html) - A package for formatting your code
- [ESLint](https://eslint.org/docs/user-guide/getting-started) - A package for checking your JavaScript and TypeScript code for errors
- [React](https://reactjs.org/docs/getting-started.html) - A library for building web applications using JavaScript
- [Create React App](https://create-react-app.dev/) - A package that makes it easy to get started with React
### Practices
We recommend that you follow these practices:
- [Conventional Commits](https://www.conventionalcommits.org/) - A specification for adding human and machine readable meaning to commit messages
## Files
The [Editor Template - Create React App and TypeScript](https://github.com/standardnotes/editor-template-cra-typescript) has the following files:
```none
editor-template-cra-typescript
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── package.json
├── tsconfig.json
├── yarn.lock
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ ├── package.json
│ ├── robots.txt
│ └── sample.ext.json
└── src
├── Editor.test.tsx
├── index.scss
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
├── reportWebVitals.ts
├── setupTests.ts
├── components
│ └── Editor.tsx
└── stylesheets
├── dark.scss
├── main.scss
└── print.scss
```
When you are familiar with these technologies, follow the instructions in the [README.md of the Editor Template](https://github.com/standardnotes/editor-template-cra-typescript#readme).

View File

@@ -1,56 +0,0 @@
---
slug: editors
id: editors
title: Building Editors for Standard Notes
sidebar_label: Building Editors
description: Building Editors for Standard Notes
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Building Editors for Standard Notes
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
Editors are front-end extensions for Standard Notes. They provider users with a custom way to interact with the contents of their notes. For more information on editors in the Standard Notes ecosystem from the user's perspective, please see ["What are Editors?"](https://standardnotes.com/help/77/what-are-editors). In this article, we provide motivation and information about building editors.
## Why build editors
The process of building editors is like this: research, design, develop, document, publish/release, market, and repeat. Here are four big reasons to go through the process of building your own editor:
1. **Building editors is fun.** When you build editors, you can practice your creativity and imagination. Editors are like works of art that you can use to help you accomplish a goal, and creating works of art can be fun at any skill level.
2. **Building editors can help other people.** When you build editors to solve a problem that you have, there are probably other people who have similar problems. You can share your editor with them, help them solve their problems, and enjoy the feeling of knowing that you are helping others.
3. **Building editors gives you control over their features.** When you build editors, you can choose which features to implement and how they work, and you can implement them at your own pace. The engineering team at Standard Notes tends to focus on improving the core products, services, and infrastructure. If you have an idea or feature that you want to implement at the editor level of the ecosystem, then you can pursue it and have full control over it. You can have control over the future of your relationship with private productivity.
4. **Building editors is a great learning experience.** When you build editors, you can learn more about yourself and what you like to do. The entire process of building editors improves your fluency in skills that are important for a variety of career opportunities in the technology industry, including software development, software engineering, product design, product management, and technical writing. Building editors is a great way to gain more experience and to get involved in the technology industry.
The engineering team at Standard Notes works to solve many difficult problems so you, a developer of editors, do not have to worry about them. These problems include how to sync the contents of the notes between mobile and desktop devices with end-to-end encryption, how to make the sync work instantaneously, and how to make the notes secure on each device. They work to create a way for editors to communicate with the main notes application and make it easy for that communication to work offline.
Free tools such as GitHub provide a place for you to store the source code of your editor and deploy a version of it to a highly available, global content delivery network without having to worry about server management.
With Standard Notes and GitHub, you can create very sophisticated single page applications without having to worry about problems in back-end engineering. The Standard Notes community has many members who are ready and available to experience new innovations in note-taking and private productivity. When building editors in Standard Notes, you do not have to worry about not having a market for your creations. You can focus on creating and be confident that we will be excited to see what you build.
## Licensing
Standard Notes provides you with a wealth of opportunities to get started with front-end software engineering. The editors communicate with the Standard Notes application through an open-source library called the [Component Relay](https://github.com/standardnotes/component-relay).
The Component Relay is released under the [Affero General Public License Version 3.0](https://github.com/standardnotes/component-relay/blob/main/LICENSE) or any later version (AGPL-3.0-or-later). This a summary of the license provided by GitHub:
> Permissions of this strongest copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. When a modified version is used to provide a service over a network, the complete source code of the modified version must be made available.
In other words, if you build an editor with the Component Relay and give copies of it to other people or let them use it, then you are legally obligated to provide them with the source code of the editor under the same license (AGPL-3.0-or-later). A common and easy way to do this is to host the source code in a public repository on GitHub.
We use AGPL-3.0-or-later to protect the community. Without the license, large technology companies (think "big tech") and startups backed by venture capital could potentially copy Standard Notes technology, rebrand it, and use the rebranded copy to drive Standard Notes out of business, thereby eliminating the future of Standard Notes as a commercially viable product and service. The Standard Notes community depends on Standard Notes being free, open-source, and trustworthy for many years to come, and the community is important and essential to us, so we use the AGPL-3.0 license. You are free to solicit fees and donations when marketing your editor, but we ask that you respect the license to help us protect the community.
## Standard Notes as an operating system
Operating systems such as MacOS, Windows, and Linux allow users to build their own desktop apps and run them on their computer as they please. Similarly, Standard Notes allows developers to build their own editors and use them in their Standard Notes app as they please. Therefore, you can think of Standard Notes as an operating system. The possibilities for building editors (apps) for the Standard Notes operating system are almost endless. Some possibilities to get you started include editors for code, markdown, rich text, passwords, bookmarks, calendars, charts, grids, kanban boards, presentation slides, spreadsheets, timelines, to-do lists, two-factor authentication codes, and other structured secrets. Think of a problem that you want to solve, and try building an editor to solve it.
[Get Started with Building Editors →](./editors-getting-started.md/)

View File

@@ -1,44 +0,0 @@
---
slug: intro
id: intro
title: Intro to Extensions
sidebar_label: Intro to Extensions
description: Documentation for the Standard Notes Extensions.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Philosophy
Standard Notes is built on the core principle of simplicity, in hopes of optimizing for longevity. This requires a different sort of architecture and long-term thinking. In today's environment, adding new features can be as easy as installing an external library and hooking it up with just a few lines of code. It's all too common that developers, in hopes of attracting more and more users, add more and more features to their app, until inevitably, the app bloats to oblivion and becomes impossible to maintain.
Rather than tightly couple every feature we dream of building for Standard Notes into the core application code, we chose to design Standard Notes to be thoroughly extensible. This allows us to experiment with new features without polluting the core application and threatening its stability and survivability.
## Sustainability
The Extensions model is also our main fundraising method, and allows us to to offer our core privacy experience at no cost, while sustaining future development by offering advanced features through our [Extended](https://standardnotes.com/plans) program.
Most of our extensions are [open-source](https://github.com/standardnotes/plugins) and available for self-hosting. You can also learn to develop your own extensions by following the guides on this site. However, we encourage you to support the sustainability and future development of this project by [purchasing a subscription](https://standardnotes.com/plans).
## Types
There are 3 main types of extensions:
1. **Components**: Components are user interface elements that completely swap out areas of Standard Notes with custom behavior. Components include editors (such as the [Markdown Pro Editor](https://standardnotes.com/features/markdown-pro) and [Plus Editor](https://standardnotes.com/features/plus-editor)), editor stack components (like the [Action Bar](https://standardnotes.com/features/action-bar) and [FileSafe](https://standardnotes.com/features/filesafe)), and other components (like [Folders](https://standardnotes.com/features/folders) and [Quick Tags](https://standardnotes.com/features/quick-tags)).
**[Develop a component →](/extensions/building-an-extension/)**
2. **Themes**: Themes change the visual appearance of Standard Notes, and are compatible on all platforms, including desktop, web, and mobile. Some of our own themes include [Midnight](https://standardnotes.com/features/midnight) and [Solarized Dark](https://standardnotes.com/features/solarized-dark).
**[Develop a theme →](/extensions/themes/)**
3. **Actions**: Actions are an API-based extension interface that are accessible via the Actions menu in the note pane. Actions are triggered manually by the user by selecting an action from the list, and have the ability to interface with the current note and send or retrieve content from a remote server. We use actions for places such as [Listed](https://listed.to), which is our blogging platform for Standard Notes users.
**[Develop an action →](/extensions/actions/)**

View File

@@ -1,85 +0,0 @@
---
id: local-setup
title: Local Setup
sidebar_label: Local Setup
description: How to set up a development environment to build a Standard Notes extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- local setup
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
These instructions describe how to run an extension in a local environment.
Installing an extension inside Standard Notes consists of two main components:
- A JSON file that describes the extension, and includes metadata such as the extension's current version, description, hosted URL, and download URL.
- The source code for the extension.
To get your extension running locally, both of these components must be hosted on a local web server. In this guide, we'll use the command line server `http-server`.
## Steps
1. Install http-server:
```bash
npm install -g http-server
```
1. In your extension's root directory, run the following command to begin hosting your local server:
```bash
http-server -p 8001 --cors
```
The `--cors` option allows the Standard Notes app to load your extension via cross-origin resource sharing (required).
1. In your extension's root directory, create a file called `ext.json`.
1. Place, at minimum, the following key/value pairs. For the full listing of keys, see the [Publishing guide](/extensions/publishing/).
```json
{
"identifier": "org.yourdomain.my-extension",
"name": "My Extension",
"content_type": "SN|Component",
"area": "editor-editor",
"version": "1.0.0",
"url": "http://localhost:8001"
}
```
The `url` should point to where your extension's index.html is hosted on your local server.
The `area` describes what kind of extension this will be. A list of valid values can be found in the [Publishing guide](/extensions/publishing/).
1. In your browser, open http://localhost:8001/ext.json and make sure you see the JSON file content from above.
1. Copy the `url` from the JSON content and open it in your browser. Here, you should see your actual extension running. Your server will look for an `index.html` file by default.
If your main HTML file is called something different, or is not located in the root directory, simply change the `url` value in the JSON file to reflect this location. For example:
```bash
url: "http://localhost:8001/dist/index.html"
```
1. At this point, your extension is ready to be installed. Open **Standard Notes**, and click on **Extensions** in the lower left corner of the app.
1. In the bottom right of the **Extensions** window, click **Import Extension**. In the Extension Link field, enter the URL of your ext.json file. In this case, it will be `http://localhost:8001/ext.json`. Then press enter.
1. You should see a message that your extension was successfully installed. You can now scroll up in the **Extensions** window, and click **Activate** next to your extension to run it. If it is an editor, Editors can be activated via the **Editor menu** in the note panel, under the note title.
---
### Themes
If you're creating a theme, you would follow the same instructions, and for `area` in the JSON file, use "themes", and for the URL, it should link directly to your css file, i.e `http://localhost:8001/theme.css`.
### Publishing
Once you're ready to ship your extension in a production environment, check out the [Publishing guide](/extensions/publishing/) to learn more about configuring your extension to autoupdate and be installed in an offline environment.

View File

@@ -1,101 +0,0 @@
---
id: publishing
title: Publishing
sidebar_label: Publishing
description: How to publish an extension for Standard Notes.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- publish an extension
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Once you've crafted the code for your extension, you'll need to create a JSON description of your package, so that it can be imported using the Extension manager.
The JSON description must be hosted on a URL the app can reach out to. In these steps, we'll use Listed to generate a JSON endpoint.
## Steps:
1. Create a new note in Standard Notes called "my-extension.json", and enter the following as its contents:
```json
---
metatype: json
---
{
"identifier": "org.yourdomain.my-markdown-editor",
"name": "My Markdown Editor",
"content_type": "SN|Component",
"area": "editor-editor",
"version": "1.0.0",
"description": "A fully featured Markdown editor that supports live preview, a styling toolbar, and split pane support.",
"url": "https://domain.org/link-to-hosted-extension",
"download_url": "https://github.com/sn-extensions/advanced-markdown-editor/archive/1.0.184.zip",
"latest_url": "https://listed.to/my-extension-json-link",
"marketing_url": "https://standardnotes.com/features/advanced-markdown",
"thumbnail_url": "https://domain.org/editors/adv-markdown.jpg"
}
```
1. Head over to [listed.to](https://listed.to), and generate an account by clicking "Generate Author Link". Copy that link, then, in the Extensions menu in Standard Notes, click "Import Extension" in the bottom right corner. Paste in your link, and press enter, then accept.
2. Back in your "my-extension.json" note, click "Actions" in the menu bar below the note title, then click "Publish to Private Link". Click "Open Private Link" to preview your JSON endpoint.
3. In the Extensions window, click "Import Extension", and paste in your JSON endpoint, press Enter, then accept to install your extension.
### Key Descriptions
| Key | Description |
| :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| identifier | A unique, reverse domain style identifier for your extension. |
| name | The name of your extension. |
| content_type | Generally SN&#124;Component, or SN&#124;Theme if theme package. |
| area | One of \[editor-editor, editor-stack, themes, note-tags, tags-list\] |
| version | The current version of your extension. Needs to match the version in your GitHub repo's package.json file. |
| description | The description that will be displayed in the Extensions browser. |
| url | Used by the web and mobile app. This value is the location of the hosted extension. It must be a link to either an HTML file \(for components\) or a CSS file \(for themes\). |
| download_url | Used by the desktop app. Must be a zip file. Caters strongly to GitHub auto-generated release files. The zip file must contain a package.json file with at least a version key. By default, the desktop app will look for an "index.html" in the root directory of the zip file. \(Note that if using GitHub auto-generated zip files, the contents of the zip file are embedded within a folder. The desktop app automatically unnests the folder contents and moves it up one level so it is on the root level.\) If your main file is not called index.html, or if it is a CSS file, please see below in "Local Installation" under "To specify the root file of your extension" instructions. |
| marketing_url | If specified, the Extensions manager will display an "Info" button, which on click, will open a web browser to this location. |
| thumbnail_url | The image the Extensions manager will display for your extension. |
## Local Installation
Extensions in Standard Notes desktop support local installation. We recommend using GitHub releases to host your download file.
1. In your extension repository on GitHub, go to the Releases tab, then click "Draft New Release".
2. Click "Publish release" \(you don't need to enter any information\). Standard Notes will use the auto-generated "Source code \(zip\)" archive that GitHub generates.
3. Right click on "Source code \(zip\)", then choose the option to Copy Link Address. Use this value for `download_url` in your JSON contents.
By default, Standard Notes will look for the `index.html` file located in the root of the project. If your main file is not in the root, you can specify a custom path.
### To specify the root file of your extension:
1. In your extension's repository, create a file called `package.json` if it does not already exist.
2. Add the following entry anywhere in the root level of `package.json`:
```json
...
"sn": {
"main": "relative/path/to/index.html"
},
...
```
`main` can also be a CSS file if you're publishing a theme.
## Autoupdate
Standard Notes will ping the `latest_url`endpoint automatically to update extensions. For this value, you can use the same JSON endpoint you're using for `url` \(your `my-extension.json` endpoint\).
## Questions?
Join the [Discord group](https://standardnotes.com/discord) and ask away in the \#dev channel.

View File

@@ -1,28 +0,0 @@
---
title: How to use the Standard Notes StyleKit
sidebar_label: StyleKit
description: How to use the Standard Notes StyleKit when building an extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- StyleKit
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Each of the Standard Notes themes use a common set of CSS variables. With the Standard Notes StyleKit, you can use those variables inside the editors.
You can find a list of variables [here](/extensions/themes/).
## License
The Standard Notes StyleKit is licensed under the GNU [AGPL-3.0-or-later](https://github.com/sn-extensions/StyleKit/blob/master/LICENSE).
## Resources
- [GitHub](https://github.com/sn-extensions/StyleKit)
- [NPM](https://www.npmjs.com/package/sn-stylekit)

View File

@@ -1,179 +0,0 @@
---
id: themes
title: Themes
sidebar_label: Themes
description: How to build a theme for Standard Notes.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- theme
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Themes allow you to customize the look and feel of the Standard Notes app on all platforms.
You can view the [source code](https://github.com/sn-extensions/solarized-dark-theme) of our official themes in order to best understand how to create your own theme.
For how to install a theme, please see [Publishing](/extensions/publishing/).
## Creating a theme
Themes are simple CSS files which override a few variables to style the look of the application. **CSS themes will automatically work on mobile.** Your CSS file should contain content similar to the below.
_Note that font and font sizes do not apply to mobile; only desktop/web._
```css
:root {
--sn-stylekit-base-font-size: 14px;
--sn-stylekit-font-size-p: 1rem;
--sn-stylekit-font-size-editor: 1.21rem;
--sn-stylekit-font-size-h6: 0.8rem;
--sn-stylekit-font-size-h5: 0.9rem;
--sn-stylekit-font-size-h4: 1rem;
--sn-stylekit-font-size-h3: 1.1rem;
--sn-stylekit-font-size-h2: 1.2rem;
--sn-stylekit-font-size-h1: 1.3rem;
--sn-stylekit-neutral-color: #989898;
--sn-stylekit-neutral-contrast-color: white;
--sn-stylekit-info-color: #086dd6;
--sn-stylekit-info-contrast-color: white;
--sn-stylekit-success-color: #2b9612;
--sn-stylekit-success-contrast-color: white;
--sn-stylekit-warning-color: #f6a200;
--sn-stylekit-warning-contrast-color: white;
--sn-stylekit-danger-color: #f80324;
--sn-stylekit-danger-contrast-color: white;
--sn-stylekit-shadow-color: #c8c8c8;
--sn-stylekit-background-color: white;
--sn-stylekit-border-color: #e3e3e3;
--sn-stylekit-foreground-color: black;
--sn-stylekit-contrast-background-color: #f6f6f6;
--sn-stylekit-contrast-foreground-color: #2e2e2e;
--sn-stylekit-contrast-border-color: #e3e3e3;
--sn-stylekit-secondary-background-color: #f6f6f6;
--sn-stylekit-secondary-foreground-color: #2e2e2e;
--sn-stylekit-secondary-border-color: #e3e3e3;
--sn-stylekit-secondary-contrast-background-color: #e3e3e3;
--sn-stylekit-secondary-contrast-foreground-color: #2e2e2e;
--sn-styleki--secondary-contrast-border-color: #a2a2a2;
--sn-stylekit-editor-background-color: var(--sn-stylekit-background-color);
--sn-stylekit-editor-foreground-color: var(--sn-stylekit-foreground-color);
--sn-stylekit-paragraph-text-color: #454545;
--sn-stylekit-input-placeholder-color: rgb(168, 168, 168);
--sn-stylekit-input-border-color: #e3e3e3;
--sn-stylekit-scrollbar-thumb-color: #dfdfdf;
--sn-stylekit-scrollbar-track-border-color: #e7e7e7;
--sn-stylekit-general-border-radius: 2px;
--sn-stylekit-monospace-font: 'Ubuntu Mono', courier, monospace;
--sn-stylekit-sans-serif-font: -apple-system, BlinkMacSystemFont, 'Segoe UI',
'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
'Helvetica Neue', sans-serif;
--sn-stylekit-grey-1: #72767e;
--sn-stylekit-grey-2: #bbbec4;
--sn-stylekit-grey-3: #dfe1e4;
--sn-stylekit-grey-4: #eeeff1;
--sn-stylekit-grey-4-opacity-variant: #bbbec43d;
--sn-stylekit-grey-5: #f4f5f7;
--sn-stylekit-grey-6: #e5e5e5;
--sn-stylekit-accessory-tint-color-1: #086dd6;
--sn-stylekit-accessory-tint-color-2: #ea6595;
--sn-stylekit-accessory-tint-color-3: #ebad00;
--sn-stylekit-accessory-tint-color-4: #7049cf;
--sn-stylekit-accessory-tint-color-5: #1aa772;
--sn-stylekit-accessory-tint-color-6: #f28c52;
}
```
### Optional Overrides
```css
--modal-background-color: var(--sn-stylekit-background-color);
--editor-header-bar-background-color: var(--sn-stylekit-background-color);
--editor-background-color: var(--sn-stylekit-editor-background-color);
--editor-foreground-color: var(--sn-stylekit-editor-foreground-color);
--editor-title-bar-border-bottom-color: var(--sn-stylekit-border-color);
--editor-title-input-color: var(--sn-stylekit-editor-foreground-color);
--editor-pane-background-color: var(--sn-stylekit-background-color);
--editor-pane-editor-background-color: var(--sn-stylekit-editor-background-color);
--editor-pane-editor-foreground-color: var(--sn-stylekit-editor-foreground-color);
--editor-pane-component-stack-item-background-color: var(--sn-stylekit-background-color);
--text-selection-color: var(--sn-stylekit-info-contrast-color);
--text-selection-background-color: var(--sn-stylekit-info-color);
--note-preview-progress-color: var(--sn-stylekit-info-color);
--note-preview-progress-background-color: var(--sn-stylekit-passive-color-4-opacity-variant);
--note-preview-selected-progress-color: var(--sn-stylekit-secondary-background-color);
--note-preview-selected-progress-background-color: var(--sn-stylekit-passive-color-4-opacity-variant);
--items-column-background-color: var(--sn-stylekit-background-color);
--items-column-items-background-color: var(--sn-stylekit-background-color);
--items-column-border-left-color: var(--sn-stylekit-border-color);
--items-column-border-right-color: var(--sn-stylekit-border-color);
--items-column-search-background-color: var(--sn-stylekit-contrast-background-color);
--item-cell-selected-background-color: var(--sn-stylekit-contrast-background-color);
--item-cell-selected-border-left-color: var(--sn-stylekit-info-color);
--navigation-column-background-color: var(--sn-stylekit-secondary-background-color);
--navigation-section-title-color: var(--sn-stylekit-secondary-foreground-color);
--navigation-item-text-color: var(--sn-stylekit-secondary-foreground-color);
--navigation-item-count-color: var(--sn-stylekit-neutral-color);
--navigation-item-selected-background-color: rgb(253, 253, 253);
--preferences-navigation-icon-color: var(--sn-stylekit-neutral-color);
--preferences-navigation-selected-background-color: var(--sn-stylekit-info-backdrop-color);
--dropdown-menu-radio-button-inactive-color: var(--sn-stylekit-passive-color-1);
--panel-resizer-background-color: var(--sn-stylekit-secondary-contrast-background-color);
--link-element-color: var(--sn-stylekit-info-color);
```
In order to get SN to display a dock icon for your theme (a circle in the lower right corner of the app that allows you to quickly toggle themes), add the following payload into the your ext.json file when [publishing your theme](/extensions/publishing/):
```json
"dock_icon": {
"type": "circle",
"background_color": "#086DD6",
"foreground_color": "#ffffff",
"border_color": "#086DD6"
}
```
### Reloading Mobile Themes
The mobile app will download a theme once and cache it indefinitely. If you're installing your own mobile theme and make changes, you can press and hold on the theme name in the list to bring up the option to re-download the theme from the server.
### 3.9.15 Changes
Since v3.9.15, the items in the notes list use a new variable for the background color, which will partially break the look of your theme when a note is selected or is hovered upon. In order to fix this, override the `--sn-stylekit-grey-5` color to one which fits your theme. You might also need to override the `--sn-stylekit-grey-4-opacity-variant` variable if the tags inside the note item don't look correct.
## Licensing
Our themes are provided open-source mainly for educational and quality purposes. You're free to install them on your own servers, but please consider subscribing to [Standard Notes Extended](https://standardnotes.com/plans) to help sustain future development of the Standard Notes ecosystem.

View File

@@ -1,175 +0,0 @@
---
slug: configuration-options
id: configuration-options
title: Configuration options
sidebar_label: Configuration options
description: Manage options for your Standard Notes Standalone Infrastructure.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
- configuration options
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Note
After changing any of the environment variables mentioned below you will have to restart the infrastructure in order for the changes to take effect. You can do so with the following command:
```bash
$ ./server.sh stop && ./server.sh start
```
## Syncing Server JS & Syncing Server Worker
All configuration options mentioned below are controlled by environment variables located in the `.env` file.
### Basics
- `LOG_LEVEL`: the level of logs outputted by the Syncing Server JS and Syncing Server JS Worker services.
- `NODE_ENV`: Node environment in which the service is running.
### Secrets
- `AUTH_JWT_SECRET`: secret used to sign the JWT tokens that are used for authorization & authentication purposes between services.
### Ports
- `EXPOSED_PORT`: the port on which the API Gateway will run. It is your main entry point for the entire infrastructure.
### Database
- `DB_HOST`: database host.
- `DB_REPLICA_HOST`: database replica host. If no replica is supported it should point to the same host as the primary DB.
- `DB_PORT`: database port.
- `DB_USERNAME`: database username.
- `DB_PASSWORD`: database password.
- `DB_DATABASE`: database name.
- `DB_DEBUG_LEVEL`: the level of logs which are outputted in the database context. Related to TypeORM.
- `DB_MIGRATIONS_PATH`: path to migrations folder that should be run against the database. Related to TypeORM.
### Cache
- `REDIS_URL`: url to Redis node.
### Redis Async Communication (Default)
- `REDIS_EVENTS_CHANNEL`: name of the Redis Pub/Sub channel used for communication between the service and its worker.
### AWS Async Communication (Optional)
If you do not want to use Redis as the communication channel between services, you can configure your async communication to be done via Amazon Web Services.
> **Note** We do not support configuring AWS secret and access keys in the environment variables as this is generally bad practice. If you would like to utilize SNS, SQS and S3, please configure an appropriate IAM user and role for the server on which you self-host your application as [best practice](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#sharing-credentials).
- `SNS_TOPIC_ARN`: ARN of the SNS topic that the service will publish events to.
- `SNS_AWS_REGION`: AWS region of the SNS topic.
- `SQS_QUEUE_URL`: URL of the SQS queue from which a worker will consume events.
- `SQS_AWS_REGION`: AWS region of the SQS queue.
- `S3_BACKUP_BUCKET_NAME`: name of the S3 bucket on which a file backup will be performed to transfer large data between services.
- `S3_AWS_REGION`: AWS region of the S3 bucket.
### Auth Service
-`AUTH_SERVER_URL`: url to the Auth service. Default value should be kept.
### Emails
- `EMAIL_ATTACHMENT_MAX_BYTE_SIZE`: Amount of bytes allowed for daily email backup attachments.
### Revisions
- `REVISIONS_FREQUENCY`: Amount of seconds that should pass between each save of a note for a new history revision to be created.
### New Relic (Optional)
We are utilizing New Relic to monitor our infrastructure. If you wish to set up your own monitoring in New Relic you can utilize the following environment variables:
- `NEW_RELIC_ENABLED`: enable or disable New Relic agent.
- `NEW_RELIC_APP_NAME`: name of the application to show in New Relic.
- `NEW_RELIC_LICENSE_KEY`: New Relic license key.
- `NEW_RELIC_NO_CONFIG_FILE`: should be true as we do not use configuration files for New Relic and fallback to environment variables.
- `NEW_RELIC_DISTRIBUTED_TRACING_ENABLED`: enable or disable distrubuted tracing.
- `NEW_RELIC_LOG_ENABLED`: enable or disable logs in New Relic.
- `NEW_RELIC_LOG_LEVEL`: level of logs in New Relic.
## Auth & Auth Worker
All configuration options mentioned below are controlled by environment variables located in the `docker/auth.env` file.
### Basics
- `LOG_LEVEL`: the level of logs outputted by the Auth and Auth Worker services.
- `NODE_ENV`: Node environment in which the service is running.
### Secrets
- `JWT_SECRET`: secret used to sign the JWT tokens that are used for authorization & authentication purposes between client and server.
- `LEGACY_JWT_SECRET`: This parameter is a fallback for supporting old client applications that had a different authorization mechanism. You don't need to change this if you are just starting to self-host your setup and do not own a legacy client application.
- `AUTH_JWT_TTL`: Time to live in seconds for the JWT token used for communication between services.
- `PSEUDO_KEY_PARAMS_KEY`: key used to generate password nonce in the process of creating user authentication parameters.
- `ENCRYPTION_SERVER_KEY`: key used for ecrypting user server key. Must be a hex string exactly 32 bytes long e.g. `feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308`.
### Authentication and Authorization
- `ACCESS_TOKEN_AGE`: time to live (in seconds) of the access token used to communicate with the server.
- `REFRESH_TOKEN_AGE`: time to live (in seconds) of the refresh token used to obtain a new access token.
- `EPHEMERAL_SESSION_AGE`: time to live (in seconds) of an ephemeral session. Used when you sign in without the "Stay signed in" option checked.
- `MAX_LOGIN_ATTEMPTS`: number of login attempts before locking the account.
- `FAILED_LOGIN_LOCKOUT`: lockout period in seconds after maximum failed login attempts.
### Redis Async Communication (Default)
- `REDIS_EVENTS_CHANNEL`: name of the Redis Pub/Sub channel used for communication between the service and its worker.
### Syncing Server Service
-`SYNCING_SERVER_URL`: url to the Syncing Server service. Default value should be kept.
### Disabling new user registrations
- `DISABLE_USER_REGISTRATION`: disable the option to register new users on the server.
### New Relic (Optional)
We are utilizing New Relic to monitor our infrastructure. If you wish to set up your own monitoring in New Relic you can utilize the following environment variables:
- `NEW_RELIC_ENABLED`: enable or disable New Relic agent.
- `NEW_RELIC_APP_NAME`: name of the application to show in New Relic.
- `NEW_RELIC_LICENSE_KEY`: New Relic license key.
- `NEW_RELIC_NO_CONFIG_FILE`: should be true as we do not use configuration files for New Relic and fallback to environment variables.
- `NEW_RELIC_DISTRIBUTED_TRACING_ENABLED`: enable or disable distrubuted tracing.
- `NEW_RELIC_LOG_ENABLED`: enable or disable logs in New Relic.
- `NEW_RELIC_LOG_LEVEL`: level of logs in New Relic.
## API Gateway
All configuration options mentioned below are controlled by environment variables located in the `docker/api-gateway.env` file.
### Basics
- `LOG_LEVEL`: the level of logs outputted by the API Gateway service.
- `NODE_ENV`: Node environment in which the service is running.
### Routing
- `SYNCING_SERVER_JS_URL`: url to the Syncing Server JS service.
- `AUTH_SERVER_URL`: url to the Auth service.
### New Relic (Optional)
We are utilizing New Relic to monitor our infrastructure. If you wish to set up your own monitoring in New Relic you can utilize the following environment variables:
- `NEW_RELIC_ENABLED`: enable or disable New Relic agent.
- `NEW_RELIC_APP_NAME`: name of the application to show in New Relic.
- `NEW_RELIC_LICENSE_KEY`: New Relic license key.
- `NEW_RELIC_NO_CONFIG_FILE`: should be true as we do not use configuration files for New Relic and fallback to environment variables.
- `NEW_RELIC_DISTRIBUTED_TRACING_ENABLED`: enable or disable distrubuted tracing.
- `NEW_RELIC_LOG_ENABLED`: enable or disable logs in New Relic.
- `NEW_RELIC_LOG_LEVEL`: level of logs in New Relic.

View File

@@ -1,231 +0,0 @@
---
slug: docker
id: docker
title: Self-hosting with Docker
sidebar_label: Docker
description: How to self-host the Standard Notes infrastructure with Docker.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
- docker
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
This guide walks you through the process of installing the self-hosted backend of Standard Notes. In this example, we used a server running Ubuntu 20.04, with 2GB RAM and 1 CPU.
Due to mounted volumes, we recommend running the setup as a root user. If you wish to run it as a non-root user, please see Docker's [post-installation steps for Linux](https://docs.docker.com/engine/install/linux-postinstall#manage-docker-as-a-non-root-user).
If you're self-hosting on an OS other than Linux, skip to the [installation](/self-hosting/docker#install-standard-notes) instructions below.
## Prerequisities
1. Update your `apt` repositories and upgrade any out-of-date packages:
```shell
sudo apt update -y && sudo apt upgrade -y
```
2. Install Docker Engine:
```shell
# Remove any old Docker installations.
sudo apt-get remove docker docker-engine docker.io containerd runc
# Install dependencies.
sudo apt install git ca-certificates curl gnupg lsb-release -y
# Add Docker's GPG key.
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Set the Docker repo.
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine.
sudo apt update -y
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
```
3. Verify that Docker is installed:
```shell
sudo docker run hello-world
```
This should output something like:
```plaintext
...
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:13e367d31ae85359f42d637adf6da428f76d75dc9afeb3c21faea0d976f5c651
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
```
4. Verify that Docker Compose is correctly installed:
```shell
docker compose version
```
This should output something like:
```plaintext
Docker Compose version v2.6.0
```
5. Enable the `ufw` firewall:
```shell
sudo ufw enable
```
Enter `y` when prompted.
6. Enable SSH connections:
```shell
sudo ufw allow ssh
```
This should output something like:
```plaintext
Skipping adding existing rule
Skipping adding existing rule (v6)
```
7. Allow incoming TPC connections on ports `80` and `443`:
```shell
sudo ufw allow http
sudo ufw allow https
```
8. Check the status of your `ufw` settings:
```shell
ufw status verbose
```
This should output something like:
```plaintext
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
80/tcp (v6) ALLOW IN Anywhere (v6)
443/tcp (v6) ALLOW IN Anywhere (v6)
```
9. Configure a domain name (or subdomain) to point to your server's IP address. Consult your domain registration provider for how to configure your domain name.
## Install Standard Notes
1. Clone the `self-hosted` repo:
```shell
cd ~
git clone --single-branch --branch main https://github.com/standardnotes/self-hosted.git
cd self-hosted
```
1. Initialize default configuration files:
```shell
./server.sh setup
```
This will output something like:
```plaintext
Initializing default configuration
Default configuration files created as .env and docker/*.env files. Feel free to modify values if needed.
```
1. Generate random values for the necessary environment variables:
```shell
./server.sh generate-keys
```
2. **Customize ports and endpoints**:
- By default, the server will run on port `3000`. If you have a different service running on that port, you can customize the port on which you want to run the infrastructure by editing the `EXPOSED_PORT` variable in the `.env` file.
- Edit `FILES_SERVER_URL` to the final public-facing URL of your server. For example, if you are self-hosting on a server on your local network which you know has an IP of 10.0.1.2, set this value to http://10.0.1.2:3125. Read more about [configuring your setup for files.](/self-hosting/file-uploads)
3. Start the Standard Notes server process:
```shell
./server.sh start
```
Docker will start outputting lots of information about the containers it is pulling in and installing. This process took about 8 minutes on a Ubuntu 20.04 server with 2GB RAM and 1 CPU.
4. Once Docker has finished installing, the Standard Notes install script will output:
```plaintext
Infrastructure started. Give it a moment to warm up. If you wish, please run the './server.sh logs' command to see details.
```
5. Check the status of your server:
```shell
./server.sh status
```
This will output something like:
```plaintext
Services State:
NAME COMMAND SERVICE STATUS PORTS
api-gateway-standalone "./wait-for.sh auth …" api-gateway running 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp
auth-standalone "./wait-for.sh db 33…" auth running
auth-worker-standalone "./wait-for.sh db 33…" auth-worker running
cache-standalone "docker-entrypoint.s…" cache running 6379/tcp
db-standalone "docker-entrypoint.s…" db running 3306/tcp
files-standalone "./wait-for.sh db 33…" files running 0.0.0.0:3125->3000/tcp, :::3125->3000/tcp
syncing-server-js-standalone "./wait-for.sh db 33…" syncing-server-js running
syncing-server-js-worker-standalone "./wait-for.sh db 33…" syncing-server-js-worker running
```
Your Standard Notes server is ready once all the services have a `STATUS` of `Up`. This process took about 11 minutes on a Ubuntu 20.04 server with 2GB RAM and 1 CPU. If you want to control when this process finishes you can run `./server.sh wait-for-startup`.
6. You should be able now to check that the syncing server is running by checking `http://localhost:3000/healthcheck`. You must do this on the server:
```bash
curl http://localhost:3000/healthcheck
OK
```
If you changed the `EXPOSED_PORT` variable, check `http://localhost:{EXPOSED_PORT}/healthcheck`.
7. You're done!
## Securing your server
To start using your new server with the Standard Notes app at `app.standardnotes.com,` you have to configure an HTTPS reverse proxy. Head over to [Securing HTTP traffic of your Sync server](./https-support.md/) for more information on how to set up a reverse proxy.
## Using your new server
In the account menu, choose `Advanced options` and enter the address of your new server in `Custom sync server`. Then, register for a new account or log in to an existing account and begin using your private new secure Standard Notes server!

View File

@@ -1,58 +0,0 @@
---
slug: file-uploads
id: file-uploads
title: File Uploads
sidebar_label: File Uploads
description: How to setup file uploading on your Standard Notes Standalone Server.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- files
- uploads
- docker
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Requirements
In order to upload files you have to have an active subscription for your user. Read the [subscriptions](./subscriptions.md/) page on instructions how to setup a subscription for yourself.
## Troubleshooting
### Files Server URL
In your `.env` file the environment variable `FILES_SERVER_URL` has to be set to a publicly accessible url. The reason for that is that the clients are accessing the Files Server directly instead of via Api Gateway. Remember that if you are hosting your standalone instance on an external server then `localhost` is not the host that properly describes where the files server resides.
### Upload directory write permissions
The default upload directory is located inside the standalone folder under `data/uploads`. Depending on the running OS, you might encounter write permissions to that folder by the application. In that case the following commands might help:
```bash
chmod -R 775 data
mkdir -p data/uploads
sudo chmod -R 755 data/uploads
sudo chown -R 1001.1001 data/uploads
```
### Limiting Storage Quota
If you would like to limit the file upload quota for your user then make sure to run the following query on your database:
```sql
INSERT INTO subscription_settings(uuid, name, value, created_at, updated_at, user_subscription_uuid) VALUES (UUID(), "FILE_UPLOAD_BYTES_LIMIT", 10737418240, FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), (SELECT us.uuid FROM user_subscriptions us INNER JOIN users u ON us.user_uuid=u.uuid WHERE u.email="EMAIL@ADDR"));
```
Note that this is setting the limit to 10GB (10737418240 bytes) for user with email `EMAIL@ADDR`
### CloudFlare Missing Headers
When using CloudFlare in conjuction with Nginx you might encounter an issue about missing `Accept-Ranges` header which is required for file downloading. As a fix please add this to your Nginx configuration:
```
proxy_cache off;
```

View File

@@ -1,72 +0,0 @@
---
slug: getting-started
id: getting-started
title: Getting Started with Self-hosting
sidebar_label: Getting Started
description: How to get started with self-hosting Standard Notes Infrastructure.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Our self-hosted server infrastructure consists of different microservices responsible for varying functionality. The self-hosted server works as the _backend_ that processes and stores your data; it does not include the web application.
The web application is an optional process that you must spin up separately. However, you can use the [existing web app](https://app.standardnotes.com) or the official Standard Notes desktop app with your self-hosted server.
:::tip Quick start
The fastest and easiest way to get up and running is to use our automated Docker setup. All you need is a Linux server and the latest version of [Docker](https://docs.docker.com/get-started).
[Check out our Docker instructions page to get started →](./docker.md/)
:::
## Infrastructure overview
### API Gateway
The main entry point of the architecture. The API Gateway is a router and proxy for all services which are otherwise inaccessible directly. All requests from client applications go through the API Gateway to reach a target underlying service. This service is configured with your reverse proxy for public HTTPS support.
### Syncing Server
Responsible for user data and syncing operations.
### Syncing Server Worker
Responsible for asynchronous tasks the Syncing Server may offload for background processing, including email backups, revision history, and more.
### Auth
Responsible for authorization and authentication mechanisms within Standard Notes.
### Auth Worker
Responsible for asynchronous tasks related to the domain of authentication and authorization, including account deletion requests and post-registration tasks.
### Database
The database is where data is stored.
### Cache
A Redis cache node is used to store temporary data for performance optimization and auto-expiring features. In self-hosted mode, Redis is used as a communication queue between services and workers.
## Troubleshooting
If you run into any issues setting up your server, please [open an issue on GitHub](https://github.com/standardnotes/standalone/issues) or reach out on the [Standard Notes Discord](https://standardnotes.com/discord).
## Web application
If you would like to self-host the actual Standard Notes web application, visit the [repository for the Standard Notes web app on GitHub](https://github.com/standardnotes/app/tree/main/packages/web).
## Self-hosting without Docker?
Configuring the full Standard Notes architecture manually can be challenging without detailed study. We do not offer support for this method of self-hosting. [The only supported self-hosting method is to use Docker →](./docker.md/)

View File

@@ -1,93 +0,0 @@
---
slug: https-support
id: https-support
title: Securing HTTP traffic of your Sync server
sidebar_label: Securing HTTP traffic of your Sync server
description: How to secure HTTP traffic of your Standard Notes Sync server.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
- secure http traffic
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
### Introduction
These instructions will enable you to secure HTTP traffic of your standalone infrastructure, using a reverse proxy with `Nginx`.
#### Pre-requisites
- Your standalone infrastructure is running on our [docker](./docker.md/) setup
- You've installed `nginx` in your server.
- You've configured a domain name (or subdomain) to point to your server's IP address.
### Getting started
#### Setting up Nginx
1. Disable the default virtual host:
```bash
unlink /etc/nginx/sites-enabled/default
```
1. Create a new file named `standardnotes.conf` within `/etc/nginx/sites-available`:
```
server {
listen 80;
listen [::]:80;
server_name yourdomain.com;
access_log /var/log/nginx/standardnotes-access.log;
error_log /var/log/nginx/standardnotes-error.log;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
```
> **Note** Replace `yourdomain.com` with your actual domain and `3000` with the port you have specified as `{EXPOSED_PORT}` if you have changed it.
1. Enable your new site:
```bash
ln -s /etc/nginx/sites-available/standardnotes.conf /etc/nginx/sites-enabled/standardnotes.conf
```
1. Restart Nginx to apply changes
There may be different ways to restart Nginx. If you installed Nginx from Ubuntu's default repository just type:
```bash
$ sudo service nginx restart
```
1. Test your `Nginx` configuration with:
```bash
$ nginx -t
```
1. Setting up Certbot for HTTPS configuration
Go to [certbot](https://certbot.eff.org/instructions) to get and install your HTTPS certificate.
Certbot should automatically update your Nginx configuration and create SSL certificates for you.
After completing the above instructions, your Sync server should be HTTPS enabled!
## Using your secured server
In the account menu, choose `Advanced Options` and enter the address of your new server in `Sync Server Domain`.
Then, register for a new account or log into an existing account and begin using your private new secure Standard Notes server!

View File

@@ -1,47 +0,0 @@
---
slug: legacy-migration
id: legacy-migration
title: Migrating From Legacy
sidebar_label: Migrating From Legacy
description: Migrating From Legacy Guide
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Preparing a database dump
If you have previously self-hosted your setup with our legacy Syncing Server, you will need to first dump the data from your existing database. There are two ways to do this depending on whether you had a separate database or the one we provided with our Docker setup.
### Database from Docker setup
In order to create a database dump, use the following command:
```bash
$ docker exec your_db_container_id sh -c 'exec mysqldump your_database_name -uroot -p "your_password"' > ./dbdump.sql
```
> **Note:** In order to get the value of `your_db_container_id`, run `docker ps` to inspect your running containers. `your_database_name` and `your_password` have to be changed to whatever you had set up in your .env file.
### External database
Run:
```bash
mysqldump your_database_name -h your_host -u your_user -p "your_password" > ./dbdump.sql
```
> **Note** Change `your_host`, `your_database_name`, `your_user` and `your_password` to values that you use to connect to your database.
## Importing your database dump
In order to import your data to the database that our Standalone setup will create, just place your `dbdump.sql` file inside the `path-to-your-standalone/data/import` folder. The data will be imported once the setups starts.
To proceed, head over to our [Self-hosting with Docker](./docker.md/) page.

View File

@@ -1,30 +0,0 @@
---
slug: subscriptions
id: subscriptions
title: Subscriptions on your Standard Notes Standalone Server
sidebar_label: Subscriptions
description: How to add subscriptions on your Standard Notes Standalone Server.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- updating
- sync server
- docker
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Requirements
To add a subscription to your self-hosted user account, run the following commands (Replace EMAIL@ADDR with your user email):
```bash
./server.sh create-subscription EMAIL@ADDR
```
## ✨ You Should Know
Building Standard Notes has high costs. If everyone evaded contributing financially, we would no longer be here to continue to build upon and improve these services for you. Please consider [donating](https://standardnotes.com/donate) if you do not plan on purchasing a subscription.

View File

@@ -1,51 +0,0 @@
---
slug: updating
id: updating
title: Updating Standard Notes Standalone Infrastructure
sidebar_label: Updating
description: How to update Standard Notes Standalone Infrastructure.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- updating
- sync server
- docker
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Requirements
These instructions make the following assumptions:
- You have an existing standalone infrastructure running with our [docker setup](./docker.md/)
## Updating
Updating of the infrastructure essentially consists of:
- Stopping all services
- Pulling changes from Git
- Checking for env file changes: new environment variables might have been added that you will need to configure
- Downloading latest Docker image versions of Standard Notes services
- Starting the services up again
To save you all the trouble we've packed it all nicely in one command that you run by typing:
```bash
$ ./server.sh update
```
## Troubleshooting
If you encounter any problems while updating, you can nuke your setup and start over. But **you must backup your data** inside the `data/*` folder. Then, to wipe your existing setup, run:
```bash
$ ./server.sh cleanup
```
> ***WARNING*** this will permanently delete all your data, so be sure to back up your database before running this command.

View File

@@ -1,162 +0,0 @@
---
slug: encryption/003
id: encryption-003
title: Encryption Protocol Specification v003
sidebar_label: Encryption v003
description: Specification for the Standard Notes end-to-end encryption.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- encryption specification
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Version 0.0.3
It is important that there exist a separation of concerns between the server and the client. That is, the client should not trust the server, and vice versa.
Encryption keys are generated by stretching the user's input password using a [key derivation function.](https://en.wikipedia.org/wiki/Key_derivation_function)
The resulting key is split in three — the first third is sent to the server as the user's password, the second third is saved locally as the user's master encryption key, and the last third is used as an authentication key. In this setup, the server is never able to compute the encryption key or the user's original password given just a fraction of the resulting key.
Note: client-server connections must be made securely through SSL/TLS.
#### Elaboration on User model encryption related fields
| name | details |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| pw_cost | The number of iterations to be used by the KDF. The minimum for version 003 is 100,000. However note that non-native clients (web clients not using WebCrypto) will not be able to handle any more than 3,000 iterations. |
| pw_nonce | A nonce for password derivation. This value is initially created by the client during registration. |
## Key Generation
### Client Instructions
Given a user inputted password `uip`, the client's job is to generate a password `pw` to send to the server, a master key `mk` that the user stores locally to encrypt/decrypt data, and an auth key `ak` for authenticating encrypted data.
#### Login Steps
1. Client makes GET request with user's email to `auth/params` to retrieve password nonce, cost, and version.
1. Client verifies cost >= minimum cost (100,000 for 003.)
1. Client computes `pw`, `mk`, and `ak` using PBKDF2 with SHA512 as the hashing function and output length of 768 bits:
```
salt = SHA256:Hexdigest([email, "SF", version, pw_cost, pw_nonce].join(":"))
key = pbkdf2(uip, salt, sha512, 768, pw_cost) // hex encoded
pw = key.substring(0, key.length/3)
mk = key.substring(key.length/3, key.length/3)
ak = key.substring(key.length/3 * 2, key.length/3)
```
1. Client sends `pw` to the server as the user's "regular" password and stores `mk` and `ak` locally. (`mk` and `ak` are never sent to the server).
#### Registration Steps
1. Client chooses default for `pw_cost` (minimum 100,000).
1. Client generates `pw_nonce`:
```
pw_nonce = random_string(256)
```
1. Client computes `pw`, `mk`, and `ak` using step (3) from Login Steps.
1. Client registers with `email`, `pw`, `pw_cost`, `pw_nonce`, and `version`.
## Item Encryption
In general, when encrypting a string, one should use an IV so that two subsequent encryptions of the same content yield different results, and one should authenticate the data as to ascertain its authenticity and lack of tampering.
In Standard Notes, two strings are encrypted for every item:
- The item's `content`.
- The item's `enc_item_key`.
## Client-side
An item is encrypted using a random key generated for each item.
### Encryption:
Note that when encrypting/decrypting data, keys should be converted to the proper format your platform function supports. It's best to convert keys to binary data before running through any encryption/hashing algorithm.
For every item:
1. Generate a random 512 bit key `item_key` (in hex format).
2. Split `item_key` in half; set item encryption key `item_ek = first_half` and item authentication key `item_ak = second_half`.
3. Encrypt `content` using `item_ek` and `item_ak` following the instructions "Encrypting a string using the 003 scheme" below and send to server as `content`.
4. Encrypt `item_key` using the global `mk` and global `ak` following the instructions "Encrypting a string using the 003 scheme" below and send to server as `enc_item_key`.
### Decryption:
Check the first 3 characters of the `content` string. This will be the encryption version.
- If it is equal to "001", which is a legacy scheme, decrypt according to the 001 instructions found [here](https://github.com/standardfile/standardfile.github.io/blob/master/doc/spec-001.md).
- If it is equal to "002" or "003", decrypt as follows:
1. Decrypt `enc_item_key` using the global `mk` and global `ak` according to the "Decrypting a string using the 003 scheme" instructions below to get `item_key`.
2. Split `item_key` in half; set encryption key `item_ek = first_half` and authentication key `item_ak = second_half`.
3. Decrypt `content` using `item_ek` and `item_ak` according to the "Decrypting a string using the 003 scheme" instructions below.
### Encrypting a string using the 003 scheme:
Given a `string_to_encrypt`, an `encryption_key`, and an `auth_key`:
1. Generate a random 128 bit string called IV.
1. Encrypt `string_to_encrypt` using `AES-CBC-256:Base64`, `encryption_key`, and `IV`:
```
ciphertext = AES-Encrypt(string_to_encrypt, encryption_key, IV)
```
1. Generate `string_to_auth` by combining the encryption version (003), the item's UUID, the IV, and the ciphertext using the colon ":" character:
```
string_to_auth = ["003", uuid, IV, ciphertext].join(":")
```
1. Compute `auth_hash = HMAC-SHA256:Hex(string_to_auth, auth_key)`.
1. Generate the final result by combining the five components into a `:` separated string:
```
result = ["003", auth_hash, uuid, IV, ciphertext].join(":")
```
### Decrypting a string using the 003 scheme:
Given a `string_to_decrypt`, an `encryption_key`, and an `auth_key`:
1. Split the string into its constituent parts: `components = string_to_decrypt.split(":")`.
1. Assign local variables:
```
version = components[0]
auth_hash = components[1]
uuid = components[2]
IV = components[3]
ciphertext = components[4]
```
1. Ensure that `uuid == item.uuid`. If not, abort decryption.
1. Generate `string_to_auth = [version, uuid, IV, ciphertext].join(":")`.
1. Compute `local_auth_hash = HMAC-SHA256(string_to_auth, auth_key)`. Compare `local_auth_hash` to `auth_hash`. If they are not the same, skip decrypting this item, as this indicates that the string has been tampered with.
1. Decrypt `ciphertext` to get final result: `result = AES-Decrypt(ciphertext, encryption_key, IV)`.
## Server-side
For every received item:
1. (Optional but recommended) Encrypt `content` using server known key and store. Decrypt before sending back to client.
## Next Steps
Join the [Discord group](https://standardnotes.com/discord) to discuss implementation details and ask any questions you may have.
You can also email [help@standardnotes.com](mailto:help@standardnotes.com).
Follow [@standardnotes on Twitter](https://twitter.com/standardnotes) for updates and announcements.

View File

@@ -1,345 +0,0 @@
---
slug: encryption
id: encryption
title: Client Encryption API
sidebar_label: Client Encryption
description: Specification for the Standard Notes end-to-end encryption.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- encryption specification
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
The 004 protocol upgrade centers around a system that makes it easy and painless to upgrade to a future protocol version, as well as more modern cryptographic primitives.
This page is a copy of the specification file located at [github.com/standardnotes/snjs](https://github.com/standardnotes/app/blob/main/packages/snjs/specification.md).
## Introduction
The Standard Notes Protocol describes a set of procedures that ensure client-side encryption of data in such a way that makes it impossible for the server, which houses the data, to read or decrypt the data. It treats the server as a dumb data-store that simply saves and returns values on demand.
Even in scenarios when the server is under active attack, clients should be fully protected, and cannot be tricked into revealing any sensitive information.
The client and server communicate under two common procedures: authentication, and syncing.
Authentication is a one-time transfer of information between client and server. In short, clients generate a long secret key by stretching a user-inputted password using a KDF. The first half of that key is kept locally as the "master key" and is never revealed to the server. The second half of that key is sent to the server as the "account server password".
The master key is then used to encrypt an arbitrary number of items keys. Items keys are generated randomly and not based on the account password. Items keys are used to encrypt syncable data, like notes, tags, and user preferences. Items keys themselves are also synced to user accounts, and are encrypted directly with the master key.
When a user's master key changes, all items keys must be re-encrypted with the new master key. Accounts should generally have one items key per protocol version, so even in the event where many protocol upgrades are created, only a few KB of data must be re-encrypted when a user's credentials change (as opposed to completely re-encrypting many megabytes or gigabytes of data).
Data is also encrypted client-side for on-device storage. When an account is present, all local data is encrypted by default, including simple key-value storage (similar to a localStorage-like store). Persistence stores are always encrypted with the account master key, and the master key is stored in the device's secure keychain (when available).
Clients also have the option of configuring an application passcode, which wraps the account master key with an additional layer of encryption. Having a passcode enabled is referred to as having a "root key wrapper" enabled. When a root key is wrapped, it is stored in local storage as an encrypted payload, and the keychain is bypassed. This allows for secure key storage even in environments that don't expose a keychain, such as web browsers.
This document delineates client-side procedures for key management and generation, data encryption, and storage encryption. Concepts related to server syncing and server session management are outside the scope of this document. This document however wholly covers any values that a server would receive, so even though syncing and server session management is out of scope, the procedures outlined in this document should guarantee that no secret value is ever revealed to the server.
## Key Management
**There are three main concepts as related to keys:**
1. **A root key**—based on an account's user-inputted password. There exists only one root key per account.
2. **A root key wrapper**—_wraps_ a root key (encrypts it) with an additional layer. This is a local-only construct, and translates directly as an "application passcode" feature.
3. **Items keys**—used to encrypt items. There can exist many items keys, and one items key can encrypt many items. Each items key is encrypted with the root key. When the root key changes, all items keys must be re-encrypted using the new root key.
### Key Generation Flow
1. User registers with an email (`identifier`) and a `password`.
2. `password` is run through a KDF to generate a key, which is then split in two, as part of a single `rootKey`.
1. The first half is the `masterKey`.
2. The second half is the `serverPassword`.
3. Client registers user account with server using `email` and `rootKey.serverPassword`.
4. Client creates new random key `itemsKey`. This key is encrypted directly with `rootKey.masterKey`, and the encrypted `itemsKey` is assigned a UUID and uploaded to the user's account. (Each `itemsKey` is a traditional item, just like a note or tag.)
### Password change or protocol upgrade flow
**When a user changes their password, or when a new protocol version is available:**
1. Client generates new `rootKey` using account identifier and password, and thus generates new `rootKey.masterKey`, `rootKey.serverPassword`, and `keyParams`, which include the protocol version and other public information used to guide clients on generating the `rootKey` given a user password.
2. Client submits new `rootKey.serverPassword` and `keyParams` to server. Note that the changing the `serverPassword` does not necessarily invalidate a user's session. Sessions management is outside of the scope of this document.
3. Client loops through all `itemsKeys` and re-encrypts them with new `rootKey.masterKey`. All `itemsKeys` are then re-uploaded to server. Note that `itemsKeys` are immutable and their inner key never changes. The key is only re-encrypted using the new `masterKey`.
This flow means that when a new protocol version is available or when a user changes their password, we do not need to re-encrypt all their data, but instead only a handful of keys.
### Key Rotation
By default, upgrading an account's protocol version will create a new `itemsKey` for that version, and that key will be used to encrypt all data going forward. To prevent large-scale data modification that may take hours to complete, any data encrypted with a previous `itemsKey` will be re-encrypted with the new `itemsKey` progressively, and not all at once. This progressive re-encryption occurs when an item is explicitly modified by the user. Applications can also be designed to bulk-modify items during idle-capacity, without user interaction.
**When changing the account password:**
- If a new protocol version is available, changing the account password will also upgrade to the latest protocol version and thus generates a new default `itemsKey`.
- If no new protocol version is available, or if the user is already using the latest version, changing the account password generates a new `rootKey`, as well as generates a new `itemsKey`. The new `itemsKey` will be used as the default items encryption key, and will also be used to progressively re-encrypt previous data. Generating a new `itemsKey` on password change ensures backward secrecy in the case the previous account password is compromised.
## Encryption Flow
_For each_ item (such as a note) the client wants to encrypt:
1. Client generates random `item_key` (note: singular. Not related to `itemsKey`).
2. Client encrypts note content with `item_key` to form `content`.
3. Client encrypts `item_key` with default `itemsKey` as `enc_item_key`.
4. Client notes `itemsKey` UUID and associates it with encrypted item payload as `items_key_id`, and uploads payload to server.
To decrypt an item payload:
1. Client retrieves `itemsKey` matching `items_key_id` of payload.
2. Client decrypts item's `enc_item_key` with `itemsKey` to form `item_key`.
3. Client decrypts item's `content` using `item_key`.
## Authentication
Registering for an account involves generating a `rootKey` and respective `keyParams`, according to the key generation flow above. The key parameters are uploaded to the server, and include:
- unique identifier (email)
- salt seed
- protocol version
To sign into an account, clients first make a request to the server to retrieve the key params for a given email. This endpoint is public and non-authenticated (unless the account has two-factor authentication enabled). The client then uses the retrieved key params to generate a `rootKey`, and uses the `rootKey.serverPassword` to authenticate the account.
Note that by default, the client trusts the protocol version the server reports. The client uses this protocol version to determine which cryptographic primitives (and their parameters) to use for key generation. This raises the question of, what happens if a malicious server underreports an account's version in order to weaken key generation parameters? For example, if a user's account is 004, but the server reports 002, the client will proceed to generate a `serverPassword` using outdated primitives.
There are two safeguards against this scenario:
1. Older protocol versions are expired and become no longer supported after a certain period.
2. Clients may sign in with a flag known as "strict sign in" (SSI). SSI ensures that the client _always_ signs in with the client-side _hardcoded latest version_ of the protocol. For example, if a client with SNJS 004 support attempts to sign in with SSI enabled, and the server reports a protocol version of 002 for a given account, the client will refuse this sign-in, and will not proceed with key generation. SSI is a user-controlled option. Clients cannot be programmed to default to SSI, as otherwise, users would be unable to sign in to their account whenever a new protocol version is available.
## Root Key Wrapping
Root key wrapping is a local-only construct that pertains to how the root key is stored locally. By default, and with no root key wrapping, the `rootKey` is stored in the secure device keychain. Only the `rootKey.masterKey` is stored locally; the `rootKey.serverPassword` is never stored locally, and is only used for initial account registration. If no keychain is available (web browsers), the `rootKey` is stored in storage in necessarily plain format.
Root key wrapping allows the client to encrypt the `rootKey` before storing it to disk. Wrapping a root key consists of:
1. Client asks user to choose a "local passcode".
2. The local passcode is run through the same key generation flow as account registration (using a random UUID as the account identifier, in place of an email) to generate a separate new root key known as the `rootKeyWrappingKey` (which likewise consists of a `masterKey` and an unused `serverPassword`).
3. The `rootKeyWrappingKey` is used to encrypt the `rootKey` as `wrappedRootKey`. The `wrappedRootKey` (along with `wrappingKeyKeyParams`) is stored directly in storage, and the keychain is cleared of previous unwrapped `rootKey`. (Some keychains have fixed payload size limit, so an encrypted payload may not always fit. For this reason `wrappedRootKey` is always stored directly in storage.)
**To unwrap a root key:**
1. Client displays an "Enter your local passcode" prompt to user.
2. Client runs user-inputted password through key generation scheme (using stored `wrappingKeyKeyParams`) to generate a temporary `rootKeyWrappingKey`.
3. Client attempts to decrypt `wrappedRootKey` using `rootKeyWrappingKey`. If the decryption process succeeds (no errors are thrown), the client successfully unlocks application, and keeps the unwrapped `rootKey` in application memory to aid in encryption and decryption of items (or rather `itemsKeys`, to be exact).
**The purpose of root key wrapping is many-fold:**
1. To allow for secure storage of root key when no secure keychain is available (i.e web browsers).
2. Even in cases when a keychain is available, root key wrapping allows users to choose an arbitrary password to protect their storage with.
3. To allow for encryption of local storage.
4. To allow applications to introduce cryptographically-backed UI-level app locking.
When a root key is wrapped, no information about the wrapper is persisted locally or in memory beyond the `keyParams` for the wrapper. This includes any sort of hash for verification of the correctness of the entered local passcode. That is, when a user enters a local passcode, we know it is correct not because we compare one hash to another, but by whether it succeeds in decrypting some encrypted payload.
## Multi-Client Root Key Changes
Because account password changes (or, in general, root key changes) require all existing items keys to be re-encrypted with the new root key, it is possible that items keys eventually fall into an inconsistent state, such that some are encrypted with a newer root key, while others are encrypted with the new root key. Clients encountering an items key they cannot encrypt with the current account root key parameters would then reach a dead end, and users would see undecryptable data.
To recover the ability to decrypt an items key, clients can use the `kp` (key params) included the items key's authenticated_data payload. These parameters represent the the key params of the root key used to encrypt this items key.
For example, when the account password changes, and thus the root key changes, all items keys are re-encrypted with the new root key on client A. Another client (client B) who may have a valid API session, but an outdated root key, will be able to download these new items keys. However, when client B attempts to decrypt these keys using its root key, the decryption will fail. Client B enters a state where it can save items to the server (wherein those items are encrypted using its existing default readable items key), but cannot read new data encrypted with items keys encrypted with client A's root key.
When client B connects to the API with a valid session token, but an outdated root key, it will be able to download new items keys, but not yet decrypt them. However, since the key parameters for the root key underlying the items key is included in the encrypted payload, the client will be able to prompt the user for their new password.
**In general,**
A. When a client encounters an items key it cannot decrypt, whose created date is greater than any existing items key it has, it will:
1. Make an authenticated request to the server to retrieve the account's current key parameters (because we suspect that they may have changed, due to the above fact). Authenticated requests to the GET key_params endpoint bypasses the MFA requirement.
2. Verify that the incoming key params version is greater than or equal to the client's current key params version. For example, if the client's key params version is 004, but the incoming key params version is 003, the client will reject these parameters as insecure and abort this process.
3. Prompt the user for their account password, including in the prompt its reason. i.e _"Your account password was changed 3 days ago. Enter your new account password."_
4. Validate the account password based on its root key's ability to decrypt the aforementioned items key. If it succeeds, replace the client's current root key with this new root key.
At this point, this client is now in sync. It does not need to communicate with the server to handle updating its state after a password change.
If the aforementioned items key's key params are not exactly equal to the server's key params (not a logical outcome, but assuming arbitrary desync), and no items keys exists with the same key params as the server key params, it must fallback to performing the regular sign in flow to authenticate its root key (based on its `serverPassword` field).
B. When a client encounters an items key it cannot decrypt, regardless of its created date, and the server key parameters are equal to the ones the client has on hand, this indicates that the items key may be encrypted with an older root key (for whatever reason).
In such cases, the client will present a "key recovery wizard", which all attempt to decrypt the stale items key:
1. Retrieve the key parameters associated with the authenticated_data of the items key's payload.
2. Prompt the user for their account password as it was on the date the key parameters were created. For example, _"Enter your account password as it was on Oct 20, 2019, 6:15AM."_
3. Generate a root key from the account password using the relevant key params, and use that root key to decrypt the stale items key. If the decryption is successful, the client will then decrypt any items associated with that items key. It will then mark the key as needing sync.
4. When the key subsequently runs through normal syncing logic, it will then proceed to be encrypted by the account's current root key, and synced to the account.
The above procedure represents a "corrective" course of action in the case that the sync following a root key change, where all items keys must be re-encrypted with the new root key, fails silently and results in inconsistent data.
Note that the difference between case A and case B is that in case A, we prompt the user for their account password and **update our client's root key** with the generated root key, if it is valid. In case B, we generate a temporary root key for decryption purposes only, but discard of the root key after our decryption. This distinction is important because in case A, the server will be required to return key parameters with version greater than or equal to the user's current version, but in case B, key parameters can be arbitrarily old. However, because in this case the root key is not used for anything other than transient read operations, we can accept protocol versions no matter how outdated they are.
### Expired Sessions
When a client encounters an invalid session network response (typically status code 498), it will:
1. Retrieve the latest key parameters from the server. (Note that because GETting key parameters may require MFA authentication, clients must be prepared to handle an "mfa-required" error response.)
2. Ensure the key parameter version is greater than or equal to the version the client currently has on hand.
3. Prompt the user for their account password, indicating the reason. i.e _"Your session has expired. Please re-enter your account password to restore access to your account."_
4. Proceed with normal sign in flow.
## Storage
**There exists three types of storage:**
1. **Value storage**—values such as user preferences, session token, and other app-specific values.
2. **Payload storage**—encrypted item payloads (such as notes and tags).
3. **Root key storage**—the primary root key.
How data is stored depends on different key scenarios.
### Scenario A
_No root key and no root key wrapper (no account and no passcode)_
- **Value storage**: Plain, unencrypted
- **Payload storage**: Plain, unencrypted
- **Root key storage**: Not applicable
### Scenario B
_Root key but no root key wrapper (account but no passcode):_
- **Value storage**: Encrypted with root key
- **Payload storage:** Encrypted with root key
- **Root key storage**:
- With device keychain: Plainly in secure keychain
- With no device keychain: Plainly in device storage
### Scenario C
_Root key and root key wrapper (account and passcode):_
- **Value storage**: Encrypted with root key
- **Payload storage**: Encrypted with root key
- **Root key storage**: Encrypted in device storage
### Scenario D
_No root key but root key wrapper (no account but passcode):_
- **Value storage**: Encrypted with root key wrapper
- **Payload storage**: Encrypted with root key wrapper
- **Root key storage**: Not applicable
## 003 Migration
For the most part, SNJS does not branch off into different modes of behavior for different protocol versions (apart from the version specific operators). This means that new constructs in 004, like items keys, are also used in 003. This is accomplished via migrations that are performed when the application detects older data state.
In particular, when SNJS detects a pre-existing 003 account (before the user even has the chance to perform the protocol upgrade), a migration will be triggered that creates a default `itemsKey` using the account's current `rootKey.masterKey`:
```
itemsKey = { itemsKey: rootKey.masterKey, version: '003' }
```
This `itemsKey` is encrypted as usual using `rootKey.masterKey`, and synced to the user's account. When the user eventually performs the 004 upgrade (by entering their account password when prompted), a new `itemsKey` will be created as a default for 004. However, their previously created 003 `itemsKey` will continue to exist, so that data previously encrypted with 003 will still be decryptable.
## Cryptography Specifics
**Key Derivation:**
| Name | Value |
| ------------------ | -------- |
| Algorithm | Argon2id |
| Memory (Bytes) | 67108864 |
| Iterations | 5 |
| Parallelism | 1 |
| Salt Length (Bits) | 128 |
| Output Key (Bits) | 512 |
**Encryption:**
| Name | Value |
| ------------------- | ------------------ |
| Algorithm | XChaCha20+Poly1305 |
| Key Length (Bits) | 256 |
| Nonce Length (Bits) | 192 |
### Root Key Derivation Flow - Specifics
Given a user `identifier` (email) and `password` (user password):
1. Generate a random salt `seed`, 256 bits (`hex`).
2. Generate `salt`:
1. `hash = SHA256Hex('identifier:seed')`
2. `salt = hash.substring(0, 32)`
3. Generate `derivedKey = argon2(password, salt, ITERATIONS, MEMORY, OUTPUT_LENGTH) `
4. Generate `rootKey` as:
```
{
masterKey: derivedKey.firstHalf,
serverPassword: derivedKey.secondHalf,
version: '004'
}
```
5. For account registration, `identifier`, `seed`, `serverPassword`, and `version` must be uploaded to the server.
**Understanding the salt `seed`:**
Our threat model is intended to distrust the server as much as possible. For this reason, we do not want to blindly trust whatever salt value a server returns to us. For example, a malicious server may attempt to mass-weaken user security by sending the same salt for every user account, and observe what interesting results the clients send back. Instead, clients play a more significant role in salt generation, and use the value the user inputs into the email field for salt generation.
At this point we have `salt = generateSalt(email)`. However, we'd ideally like to make this value more unique. Emails are globally unique, but well-known in advance. We could introduce more variability by also including the protocol version in salt computation, such as `salt = generateSalt(email, version)`, but this could also be well-accounted for in advance.
The salt `seed` serves as a way to make it truly impossible to know a salt for an account ahead of time, without first interacting with the server the account is hosted on. While retrieving a `seed` for a given account is a public, non-authorized operation, users who configure two-factor authentication can proceed to lock this operation so that a proper 2FA code is required to retrieve the salt `seed`. Salts are thus computed via `salt = generateSalt(email, seed)`.
### Items Key Generation Flow
1. Generate random `hex` string `key`, 256 bits.
2. Create `itemsKey = {itemsKey: key, version: '004'}`
### Encryption - Specifics
An encrypted payload consists of:
- `items_key_id`: The UUID of the `itemsKey` used to encrypt `enc_item_key`.
- `enc_item_key`: An encrypted protocol string joined by colons `:` of the following components:
- protocol version
- encryption nonce
- ciphertext
- authenticated_data
- `content`: An encrypted protocol string joined by colons `:` of the following components:
- protocol version
- encryption nonce
- ciphertext
- authenticated_data
**Procedure to encrypt an item (such as a note):**
1. Generate a random 256-bit key `item_key` (in `hex` format).
2. Encrypt `item.content` using `item_key` to form `content`, and `{ u: item.uuid, v: '004', kp: rootKey.key_params IF item.type == ItemsKey }` as `authenticated_data`, following the instructions _"Encrypting a string using the 004 scheme"_ below.
3. Encrypt `item_key` using the the default `itemsKey.itemsKey` to form `enc_item_key`, and `{ u: item.uuid, v: '004', kp: rootKey.key_params IF item.type == ItemsKey }` as `authenticated_data`, following the instructions _"Encrypting a string using the 004 scheme"_ below.
4. Generate an encrypted payload as:
```
{
items_key_id: itemsKey.uuid,
enc_item_key: enc_item_key,
content: content,
}
```
### Encrypting a string using the 004 scheme:
Given a `string_to_encrypt`, an `encryption_key`, `authenticated_data`, and an item's `uuid`:
1. Generate a random 192-bit string called `nonce`.
2. Encode `authenticated_data` as a base64 encoded json string (`base64(json(authenticated_data))`) where the embedded data is recursively sorted by key for stringification (i.e `{v: '2', 'u': '1'}` should be stringified as `{u: '1', 'v': '2'}`), to get `encoded_authenticated_data`.
3. Encrypt `string_to_encrypt` using `XChaCha20+Poly1305:Base64`, `encryption_key`, `nonce`, and `encoded_authenticated_data`:
```
ciphertext = XChaCha20Poly1305(string_to_encrypt, encryption_key, nonce, encoded_authenticated_data)
```
4. Generate the final result by combining components into a `:` separated string:
```
result = ['004', nonce, ciphertext, encoded_authenticated_data].join(':')
```
## Next Steps
Join the [Discord group](https://standardnotes.com/discord) to discuss implementation details and ask any questions you may have.
You can also email [help@standardnotes.com](mailto:help@standardnotes.com).
Follow [@standardnotes on Twitter](https://twitter.com/standardnotes) for updates and announcements.

View File

@@ -1,16 +0,0 @@
---
slug: template/
id: template
title: Template
sidebar_label: Template
description: Template
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -1,56 +0,0 @@
---
slug: import-backups
id: import-backups
title: How to import a backup without being signed in
sidebar_label: Import backups
description: How to import a backup in the Standard Notes web and desktop app without being signed in.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- web
- desktop
- import backups
- backups
- import
- data
- account
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
We are aware of issues with importing backups belonging to another account when that account is still registered, such as when you are [changing your account email](https://standardnotes.com/help/7/how-can-i-change-my-account-email). If you are signed in to your new account, some tags are duplicated and are not properly assigned to notes and a significant number of notes, tags and/or editors are not imported. We are working on a fix. As a temporary workaround, you can import the backup while signed out of the app before signing in to the new account. Then, when you sign in, choose to merge local data (this is an option that is on by default).
:::note
For the best experience, use a backup that was exported from the [web](https://app.standardnotes.com) or desktop app. A backup that was generated from [CloudLink](https://standardnotes.com/help/27/how-do-i-enable-dropbox-google-drive-or-onedrive-backups) may not work as well.
:::
## Clear your account
If you are trying to change your sync account email from an old email to a new email and tried to import a backup but encountered the issues described above, then you probably want to clear your **new** account before reimporting the backup. Please **do not** clear your **old** account until your backup has been successfully imported in your new account.
There are two ways to clear your account: delete everything using the free Batch Manager extension or use the [reset tool](https://standardnotes.com/reset). The reset tool ensures that your new account is completely empty before you reimport your backup.
## Reimport backup
After clearing your new account, please download a backup from your old account and import it into your new account while merging local data:
1. Open the [web app](https://app.standardnotes.com) or desktop app.
2. Export an encrypted or decrypted backup from your old account via the **Account** menu at the bottom left corner of the app.
3. After you have an export downloaded on your computer, click **Sign Out**, then click **Clear session data**.
4. Click on the **Account** menu. Without signing in, click on **Import Backup** to import your backup into the app. If you are importing an encrypted backup, enter your previous password when prompted.
5. Sign in to your new account with Merge local data **on** (this is on by default).
a. If you cleared your new account using the Batch Manager, then you can sign in to your new account. Click on **Sign In**, enter your account's credentials, and make sure the **Merge local data** option is selected before clicking on **Sign In** again.
b. If you cleared your account using the [reset tool](https://standardnotes.com/reset), then you need to register your new account again. Click on **Register**, enter your email and password, confirm the password, and make sure the **Merge local data** option is selected before clicking on **Register** again.
6. After signing in, verify that all your notes, tags, and other items were properly imported into your account.

View File

@@ -1,82 +0,0 @@
---
slug: reset-apps
id: reset-apps
title: How do I reset the app?
sidebar_label: Reset the Apps
description: How to reset the Standard Notes app on each platform.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- reset apps
- delete
- clear storage
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
If you are experiencing issues with signing in to the apps, you may want to clear the apps storage. Before proceeding, please sign in to the [web](https://app.standardnotes.com) app (in a different browser, if needed) to confirm that notes, tags, and other items on your notes syncing account that were modified or created on another device were saved and synced to our servers.
:::warning
If you do not have an account, do not proceed with these steps until a fix has been made available. You may have some luck in simply entering in your password or passcode several times until the app unlocks.
:::
## Mobile
**Please note:** Reinstalling the app on mobile devices may download a backup of your app from the cloud and you might run into the same issue again. Clearing the cache alone is usually not enough to reset the app. If you have an [account](https://app.standardnotes.com) with us and can verify that your changes have synced, then clearing the app's storage will not result in the loss of your notes.
### [Android](https://play.google.com/store/apps/details?id=com.standardnotes&hl=en_US)
1. Locate the Standard Notes app in the app drawer.
2. Press and hold the apps icon until a small menu appears.
3. Tap on **App Info**.
4. Locate and use the option to delete/clear the apps storage (and cache).
5. Relaunch the app and sign in again.
**Please note:** Do not reinstall the app afterwards.
### [iOS](https://apps.apple.com/us/app/standard-notes/id1285392450)
1. Please open the **Settings** app, and navigate to your iCloud backups with the following path:
**[Your name and iCloud account username (at the top)] → iCloud → Manage Storage → Backups → [Tap on the correct device]**
2. Wait for the list of apps to load.
- Tap on “Load all/more apps” if necessary and wait for the list to expand once more
3. Finally, locate the Standard Notes app and tap on the toggle button to remove the current and all future backups of the app from iCloud.
4. From there, please reinstall the app and sign in again.
- You can enable the setting to backup the app to iCloud again, if you would like.
## [Desktop](https://standardnotes.com/download) and [Web](https://app.standardnotes.com)
The methods for clearing the storage of the [desktop](https://standardnotes.com/download) and [web](https://app.standardnotes.com) apps are largely similar but require toggling the developer tools, which requires different sets of steps.
### Desktop
1. Click on the menu button/bar.
2. Click on the **View** dropdown menu.
3. Click on the **Toggle Developer Tools** option.
4. Expand it until it reveals a button for tab named **Application**.
5. Click on **Clear Storage**.
6. Click on **Clear site data**.
7. Sign in again.
### Web
#### Chromium-based browsers, such as Google Chrome, Vivaldi, and Microsoft Edge
1. Right-click anywhere on the web app.
2. Click on **Inspect Element** in the context menu.
3. Expand the Developer Tools view that appears until it reveals a button for tab named **Application**.
4. Click on **Clear Storage**.
5. Click on **Clear site data**.
6. Sign in again.
#### Firefox-based browsers
1. Right-click anywhere on the web app.
2. Click on **Inspect Element** in the context menu.
3. Click on the **Storage tab**.
4. Expand each item on the left side.
5. Check on all of the items at the lowest levels by clicking on each one, and then click on the items that appear on the right (if there are any) and delete them.
6. Sign in again.

View File

@@ -1,63 +0,0 @@
---
title: Action Bar
sidebar_label: Action Bar
description: How to use the Standard Notes Action Bar.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- action bar
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
The Action Bar is a utility bar for Standard Notes. It provides information about the current note and a few useful actions.
## Information
The Action Bar provides the following information:
- The title of the note
- The date and time when the note was created
- The date and time when the note was last updated
- The approximate number of words in the note (as determined by the spaces)
- The approximate number of paragraphs in the note (as determined by line skips)
- The number of characters in the note
- The approximate read time of the note in minutes (approximated with a read speed of 200 words per minute)
## Actions
The Action Bar provides the following actions:
- Copy the current date to your clipboard in the format `Month/Date/Year, Hour:Minute AM/PM`
- Duplicate the note
- Copy the contents of the note to your clipboard
- Save the contents of the note to a file.
- Email the note. This creates and clicks a `mailto:` link with the note's title as the subject of the email and the note's content as the body of the email.
:::tip
The default extension for saving a note is `.txt`. You can also save your files with `.md`, `.html`, and `.tex` extensions. If you frequently export a note with a particular file extension, you can add the extension to the title of the note. For example, naming your note `My Blog Post.md` or `Book.tex` will export the notes as `.md` and `.tex` files, respectively.
:::
## Development
The Action Bar is written in JavaScript and compiled with Grunt.
1. Clone the [action-bar](https://github.com/standardnotes/action-bar) repository from GitHub.
2. Run `npm install` to install required dependencies.
3. Ensure that either the Standard Notes desktop app is available for use or the web app is accessible. Use both locally or with an Extended account (or the extension will not load).
4. Follow the instructions [here](/extensions/local-setup/) to setup the extension locally.
- For the `area` property, use `editor-stack`
5. Begin development! Upon making any changes to the code, run `grunt` to build the files to the `dist` folder.
## License
The Action Bar is licensed under the GNU [AGPL-3.0-or-later](https://github.com/standardnotes/action-bar/blob/master/LICENSE).
## Resources
- [GitHub](https://github.com/standardnotes/action-bar)

View File

@@ -1,28 +0,0 @@
---
title: Autocomplete Tags
sidebar_label: Autocomplete Tags
description: How to use the Standard Notes quick tags extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Work more efficiently by quickly selecting from a live list of tags while you type. Supports keyboard shortcuts and folders.
## Development
The Autocomplete Tags component is written in JavaScript is compiled with Grunt.
## License
The Autocomplete Tags component is licensed under the GNU [AGPL-3.0 or Later](https://github.com/sn-extensions/autocomplete-tags/blob/master/LICENSE)
## Resources
- [GitHub](https://github.com/sn-extensions/autocomplete-tags)

View File

@@ -1,67 +0,0 @@
---
id: 'bold-editor'
title: Bold Editor
sidebar_label: Bold Editor
description: How to use the Standard Notes bold editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- bold editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
export const Highlight = ({children, color}) => ( <span style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}> {children} </span>
//#28a745 green
//#1877F2 blue
//#ffc107 yellow
//#dc3545 red
);
## Introduction
The Bold Editor is a [derived editor](https://standardnotes.com/help/77/what-are-editors) for Standard Notes. It is derived from the [Redactor](https://imperavi.com/redactor). The instructions for adding in-line images, videos, and audio recordings are available [here](https://standardnotes.com/help/71/how-do-i-add-images-to-my-notes).
### <Highlight color="#1877F2">Warning</Highlight>
Changing the editor for a note to the Bold Editor will add `html` tags around each line of your text. These tags will be present when you change the editor back to a markdown editor.
If you want to convert a note from HTML to plaintext, you will need to remove these tags manually or by using a separate text editor such as [VS Code](https://code.visualstudio.com/) or [Atom](https://atom.io) because we do not yet have a "find and replace" feature. If you would like to test the Bold Editor with your note, you can restore a previous copy of the note in the Session History. If you restore an old copy, then any changes made with the Bold Editor will be lost.
The search feature in the [Minimist editor](https://standardnotes.com/features/markdown-minimist) may help you remove the tags manually.
## Keyboard Shortcuts
| Result | Shortcut |
| :----------------- | :------------------------------- |
| Remove format | Ctrl/⌘ + m |
| Undo | Ctrl/⌘ + z |
| Redo | Ctrl/⌘ + y or Shift + Ctrl/⌘ + z |
| Bold | Ctrl/⌘ + b |
| Italic | Ctrl/⌘ + i |
| Superscript | Ctrl/⌘ + h |
| Subscript | Ctrl/⌘ + l |
| Link | Ctrl/⌘ + k |
| Ordered List | Ctrl/⌘ + Shift + 7 |
| Unordered List | Ctrl/⌘ + Shift + 8 |
| Outdent | Ctrl/⌘ + [ |
| Indent | Ctrl/⌘ + ] |
| Normal (Pagagraph) | Ctrl/⌘ + Alt + 0 |
| Heading 1 | Ctrl/⌘ + Alt + 1 |
| Heading 2 | Ctrl/⌘ + Alt + 2 |
| Heading 3 | Ctrl/⌘ + Alt + 3 |
| Heading 4 | Ctrl/⌘ + Alt + 4 |
| Heading 5 | Ctrl/⌘ + Alt + 5 |
| Heading 6 | Ctrl/⌘ + Alt + 6 |
## Further Resources
- [GitHub](https://github.com/standardnotes/bold-editor) - Development instructions, license (AGPL-3.0-or-later), and source code.

View File

@@ -1,15 +0,0 @@
---
id: 'code-editor'
title: Code Editor
sidebar_label: Code Editor
description: How to use the Standard Notes code editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- code editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -1,56 +0,0 @@
---
slug: code-pro
id: code-pro
title: Code Pro Editor (Beta)
sidebar_label: Code Pro Editor
description: How to use the Standard Notes Code Pro Editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Code Pro Editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Code Pro is a [derived editor](https://standardnotes.com/help/77/what-are-editors) for [Standard Notes](https://standardnotes.com), a free, [open-source](https://standardnotes.com/knowledge/5/what-is-free-and-open-source-software), and [end-to-end encrypted](https://standardnotes.com/knowledge/2/what-is-end-to-end-encryption) notes app.
Code Pro is a code editor powered by the [Monaco Editor](https://microsoft.github.io/monaco-editor/) (Visual Studio Code). It is meant for writing Markdown and 60 other programming languages.
Code Pro is not meant to be used on mobile devices.
## Features
- Syntax highlighting for Markdown and more than 60 other programming languages
- Languages supported: abap, aes, apex, azcli, bat, c, cameligo, clojure, coffeescript, cpp, csharp, csp, css, dart, dockerfile, fsharp, go, graphql, handlebars, hcl, html, ini, java, javascript, json, julia, kotlin, less, lexon, lua, markdown, mips, msdax, mysql, objective-c, pascal, pascaligo, perl, pgsql, php, plaintext, postiats, powerquery, powershell, pug, python, r, razor, redis, redshift, restructuredtext, ruby, rust, sb, scala, scheme, scss, shell, sol, sql, st, swift, systemverilog, tcl, twig, typescript, vb, verilog, xml, yaml
- Autocompletion
- Intelligent autocompletion for CSS, JavaScript, JSON, Less, Handlebars, HTML, Razor, SCSS, and TypeScript
- Sophisticated search and replace
- Prettier formatting for CSS, GraphQL, Markdown, HTML, JavaScript, Less, TypeScript, Sass, and Yaml. Built-in formatting for JSON.
- Settings: language, font size, tab size (`2` or `4`), theme (light, dark, high contrast, or SN themed), and word wrap (`on`, `off`, and `bounded`)
- Per-note settings
- Buttons to save and load default settings
## Keyboard Shortcuts
Perform these shortcuts with the editor
| Action | Shortcut |
| :-------------------------------------------------------------- | :----------------------------------------------- |
| Toggle word wrap between `on` and `off` (bounded is unaffected) | <kbd>Alt</kbd> + <kbd>Z</kbd> |
| Format code with Prettier^ | <kbd>Shift</kbd> + <kbd>Alt</kbd> + <kbd>F</kbd> |
| Toggle Tab Key Moves Focus (vs tab spacing) | <kbd>Ctrl/⌘</kbd> + <kbd>M</kbd> |
^ For CSS, GraphQL, Markdown, HTML, JavaScript, Less, TypeScript, Sass, and Yaml. Some languages, such as JSON, have built-in formatters.
Each time the editor refreshes (e.g., toggling word wrap, formatting code), the editor remembers your position (line number and column) and centers it on the screen if it's not already in focus.
## Settings
The settings for each note are saved automatically after they are changed. Loading default settings will sync the note's settings with the default settings and save automatically.
### Themes
The Monaco Editor comes with three themes: `vs` (a white/light theme), `vs-dark` (a dark theme like the default theme for VS Code), and `hc-black` (a high contrast dark theme). There is also one more option: `sn-theme`. The `sn-theme` option takes either `vs` or `vs-dark` depending on your system theme and adjusts some of the colors (e.g., link colors) to match the theme. The `sn-theme` is still a work-in-progress.

View File

@@ -1,138 +0,0 @@
---
slug: filesafe/aws
id: filesafe-aws
title: FileSafe with Amazon S3
sidebar_label: Amazon S3
description: How to use Amazon S3 with Standard Notes FileSafe.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- FileSafe
- AWS Amazon S3
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
This guide will help you set up your AWS account with an S3 bucket and User to be used by the Standard Notes FileSafe extension.
## Log In
Start by logging into your [AWS account](https://console.aws.amazon.com). Make sure to pick your preferred region in the top right corner.
![image](/img/filesafe/aws/preferred-region.png)
Check the [list of regions](https://docs.aws.amazon.com/general/latest/gr/rande.html) and take note of the region code you picked. In the example above I picked **Paris** so my region code is **eu-west-3**.
## Create the S3 bucket
In the Services dropdown of the AWS console pick the **S3 storage service** and click on the **Create bucket** button.
Choose a name for your bucket and select the region you chose before.
![image](/img/filesafe/aws/create-bucket.png)
You can **skip directly to step 3** if you don't want any additional features such as _versioning_ or _logging_ for your bucket.
In **step 3** make sure to keep **_Block all public access_** selected.
After confirming your settings you should see your new bucket.
![image](/img/filesafe/aws/block-all-public-access.png)
## Create the IAM user with the required permissions
We'll start by creating the read/write policy for the new bucket, then, we'll create a group with that policy and finally create our user and assign it to our group.
## Create the policy
In the services dropdown select **IAM** and go to **Policies.** Click on the **Create policy** button and you should see the following screen:
![image](/img/filesafe/aws/create-policy.png)
Now click on the JSON tab and add the following policy configuration:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::<bucket-name>"]
},
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": ["arn:aws:s3:::<bucket-name>/*"]
}
]
}
```
Make sure to replace **`<bucket-name>`** with the name of the bucket you created in the previous step.
Click on the **Review policy** button, pick the name for your policy and create the policy.
![image](/img/filesafe/aws/review-policy.png)
## Create the group
Back on the **IAM** console pick **Groups** from the side menu and click on **Create New Group.**
Choose your group name, click N**ext Step** and pick the policy you created previously.
![image](/img/filesafe/aws/create-group.png)
![image](/img/filesafe/aws/attach-policy-to-group.png)
After reviewing your configuration create the group.
![image](/img/filesafe/aws/review-group.png)
## Creating the user
Back on the **IAM** console pick **Users** from the side menu and click on **Add user**.
Choose a user name and make sure to select **Programmatic access.**
![image](/img/filesafe/aws/create-user.png)
On the next screen add our user to the group we just created.
![image](/img/filesafe/aws/add-user-to-group.png)
You can skip the tags screen and create the user.
In the success screen make sure to either download the **CSV** or copy the **Access key ID** and **Secret access key** as you won't be able to view the secret access key in the future.
![image](/img/filesafe/aws/copy-access-key.png)
## Standard Notes
In **Standard Notes** pick **Add New** from the **Integrations** section in **FileSafe**.
![image](/img/filesafe/aws/add-integration-in-sn.png)
In the **Link Integrations** page pick the **AWS S3** option and fill all the required information.
![image](/img/filesafe/aws/link-integrations.png)
![image](/img/filesafe/aws/submit-integration.png)
Copy the code generated to **Standard Notes** and you should see the **AWS S3** integration in the app.
![image](/img/filesafe/aws/test-integration-1.png)
### Testing the integration
Simply attach a file to your note to test the integration. The file should be available in **Standard Notes** and in your **S3 bucket** under **FileSafe**:
![image](/img/filesafe/aws/test-integration-2.png)
![image](/img/filesafe/aws/view-test-integration-in-aws.png)
Congratulations! **FileSafe** is now integrated with your **S3 bucket**.

View File

@@ -1,55 +0,0 @@
---
title: Folders
sidebar_label: Folders
description: How to use the Standard Notes folders extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
You can use the [Folders](https://standardnotes.com/features/folders) extension to create nested folders from your tags with easy drag and drop. Folders also supports [Smart Tags](/usage/tags/), which allow you to build custom filters for viewing your notes.
## Terminology
The words _tags_ and _folders_ can be used interchangeably in the Standard Notes context. However, when discussing their use on the web and desktop apps, it is helpful to distinguish them. The labels located at the top of the app are referred to as _tags_ and the labels located on the left side of the app are referred to as _folders_.
When tags are nested inside other tags, the outer/higher level tag is called a _parent_ tag and the inner/lower level tag is called a _child_ tag.
Nested tags are recorded in a `Parent.Child` format and appear that way on mobile.
## Usage
If you add a child tag to a note, the tag's parent tag may or may not be added depending on how you add the child tag.
If you create a note in a folder (e.g., the **All** folder) and add a child tag using the [Quick Tags](/usage/tags/) extension, its parent tag will also be added. If you create a note in the child folder, the child tag will automatically be added to the note, but the tag for the parent folder will not be added.
For example, if your **Recipes** folder includes the **Cakes** and **Pasta** tags, then there are two main ways to approach adding "Spaghetti Recipe" note to the **Pasta** folder:
- Create a note in the **Pasta** folder. One tag will be added: **Recipes.Pasta**.
- Create the note a folder other than the **Pasta** folder and type "Pasta" into the top input field for tags. Two tags will be added: **Recipes** and **Recipes.Pasta**.
:::note
You may need to refresh your app after deleting a folder/tag.
:::
## Development
The Folders Component is written in JavaScript and compiled with Grunt.
## License
The source code for the Folders Component is licensed under GNU [AGPL-3.0-or-later](https://github.com/standardnotes/folders-component/blob/master/LICENSE).
## Resources
- [GitHub](https://github.com/standardnotes/folders-component)

View File

@@ -1,79 +0,0 @@
---
slug: /usage
id: general
title: General Usage
sidebar_label: General Usage
description: How to use the Standard Notes app.
keywords:
- standard notes
- notes app
- end-to-end encryption
- usage
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Keyboard Shortcuts
### Edit
| Result | Windows/Linux | Mac |
| :-------------------------------- | :--------------- | :------------------ |
| Undo | Ctrl + Z | ⌘ + Z |
| Redo | Ctrl + Y | ⌘ + Y |
| Cut | Ctrl + X | ⌘ + X |
| Copy | Ctrl + C | ⌘ + C |
| Paste | Ctrl + V | ⌘ + V |
| Paste and Match Style\* | Ctrl + Shift + V | ⌘ + Shift + V |
| Select All | Ctrl + A | ⌘ + A |
| Jump to the beginning of the note | Ctrl + Home | ⌘ + Home or ⌘ + Up |
| Jump to the end of the note | Ctrl + End | ⌘ + End or ⌘ + Down |
\*Paste and Match Style only works with Rich Text Editors such as the Bold and Plus editors
### View
| Result | Windows/Linux | Mac |
| :--------------------- | :------------------------ | :--------------------- |
| Reload | Ctrl + R | ⌘ + R |
| Toggle Developer Tools | Ctrl + Shift + I | ⌘ + Shift + I |
| Actual Size | Ctrl + 0 | ⌘ + 0 |
| Zoom In | Ctrl + Shift + `+` (plus) | ⌘ + Shift + `+` (plus) |
| Zoom Out | Ctrl + `-` (minus) | ⌘ + `-` (minus) |
| Toggle Full Screen | F11 | F11 |
| Hide Menu Bar\* | Alt + M | Alt + M |
\*The Hide Menu Bar shortcut is available only when not using the Themed Menu Bar (window decoration). To toggle the Themed Menu Bar on Windows and Linux, visit **☰** > View > Themed Menu Bar.
### Window
| Result | Windows/Linux | Mac |
| :------- | :------------ | :---- |
| Minimize | Ctrl + M | ⌘ + M |
| Close | Ctrl + W | ⌘ + W |
### Reserved
These keyboard shortcuts have not been implemented but are reserved for future use. Developers who are interested in implementing keyboard shortcuts for their extensions should avoid using these shortcuts.
- Ctrl/⌘ + `T`
- Ctrl/⌘ + Shift + `F`
- Ctrl/⌘ + Shift + `L`
## Note Options
What does it mean to lock or protect a note?
<!-- Copied from https://standardnotes.com/help/54/what-does-it-mean-to-lock-or-protect-a-note -->
Here is a list of available options on a note and what they represent:
| Option | Description |
| :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Pin** | Pinning a note will anchor it to the top of your list of notes, sorted according to the global sort order (specified in your _Options_). |
| **Archive** | Archiving a note will stash your note and hide it from your usual interface. Archived notes can be found by choosing the reserved _Archived_ view in the tags panel. Archiving a note does not affect or improve performance, as the note is still saved and loaded, but not displayed in the list of all notes or within a particular tag. Archiving is useful for notes that no longer contain actionable data, but want to be preserved for historical purposes. For example, if a note contains a list of todos, and you've completed all the todos, archiving the note would make sense. |
| **Lock** | Locking a note will put it read-only mode, which means it can't be edited or deleted until you unlock it. This is to prevent accidental modification of sensitive notes, like passwords or credentials that aren't likely to change often. |
| **Protect** | Protecting a note marks the note as sensitive, and makes it so that additional authentication is required to view the note. The form of authentication used depends on your configuration. You will be asked to authenticate with your passcode or biometrics (mobile) if you have that configured. If you do not have a passcode or biometrics configured, you will be asked to authenticate with your account password. If neither of those are configured and there is no protection source available, the protected action will proceed without authentication. In addition, protecting a note will automatically hide its preview in your list of notes. |
| **Preview** | On web and desktop, selecting the Preview option will toggle whether to hide or show the note preview in the list of notes. You can hide previews for a particular note if you'd like to conserve vertical space in your list of notes, or to hide sensitive data. However, we recommend _Protecting_ a note if you'd like to hide sensitive information. |
| **Move to Trash** | Moving a note to the trash will mark the note as deleted, and remove it from your interface. However, your note will still exist in your Trash, which you can access from your list of _Views_. From the trash, you can restore the note, or choose to delete it permanently. You can also empty the entirety of your trash by choosing **Options → Empty Trash** from the note editor panel. |

View File

@@ -1,30 +0,0 @@
---
slug: github-push
id: github-push
title: GitHub Push
sidebar_label: GitHub Push
description: How to use the Standard Notes GitHub Push extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- github push
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
The GitHub Push Action Extension pushes a note to a public or private repository. You can choose a directory, extension and commit message, as well as which repository to push to.
Required permissions for public repo access:
- public_repo
![Public access](https://user-images.githubusercontent.com/772937/92886567-9a8a8700-f3c8-11ea-9560-b1956eecdf4b.png)
Required permissions for private repo access:
- repo
![Private access](https://user-images.githubusercontent.com/772937/92886571-9bbbb400-f3c8-11ea-86d8-713f0041a5ba.png)

View File

@@ -1,122 +0,0 @@
---
id: 'markdown-basic'
title: Markdown Basic Editor
sidebar_label: Markdown Basic
description: How to use the Standard Notes Markdown Basic editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- markdown basic
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
Markdown Basic is a [custom editor](https://standardnotes.com/help/77/what-are-editors) for Standard Notes. It uses Markdown-It to parse Markdown.
## Features
- Markdown via Markdown-It
- Syntax Highlighting via Highlight.js
- Optional split pane view
- Task Lists
- Tables
- Footnotes
- Inline external images
## Installation
1. Register for an account at Standard Notes using the [Desktop App](https://standardnotes.com/download) or [Web app](https://app.standardnotes.com). Remember to use a strong and memorable password.
2. Sign up for [Standard Notes Extended](https://dashboard.standardnotes.com/member). Then, follow the instructions [here](https://standardnotes.com/help/29/how-do-i-install-extensions-once-i-ve-signed-up-for-extended) or continue.
3. Click **Extensions** in the lower left corner.
4. Under **Repository**, find **Markdown Basic**.
5. Click **Install**.
6. Close the **Extensions** pop-up.
7. At the top of your note, click **Editor**, then click **Markdown Basic**.
8. Click **Continue**.
After you have installed the editor on the web or desktop app, it will automatically sync to your [mobile app](https://standardnotes.com/download) after you sign in.
## Style Guide
| Result | Markdown |
| :----------------- | :------------------------------------------- |
| **Bold** | \*\*text\*\* or \_\_text\_\_ |
| _Emphasize_ | \*text\* or \_text\_ |
| ~~Strike-through~~ | \~\~text\~\~ |
| Link | [text]\(http://) |
| Image | ![text]\(http://) |
| `Inline Code` | \`code\` |
| Code Block | \`\`\`language <br></br>code <br></br>\`\`\` |
| Unordered List | \* item <br></br> - item <br></br> + item |
| Ordered List | 1. item |
| Task List | `- [ ] Task` or `- [x] Task` |
| Blockquote | \> quote |
| H1 | # Heading |
| H2 | ## Heading |
| H3 | ### Heading |
| H4 | #### Heading |
| Section Breaks | `---` or `***` |
## Tables
Colons can be used to align columns.
Copy this into your editor to see what it renders:
```
| Tables | Are | Cool |
| ---------------- | :-----------: | --------: |
| col 2 is | centered | \$149 |
| col 3 is | right-aligned | \$4.17 |
| privacy is | neat | \$2.48 |
| rows don't need to |be pretty| what? |
| the last line is | unnecessary | really?
| one more | row | Yay! 😆
```
## Footnotes
The Markdown Basic editor supports footnotes. The footnote links do not work properly on mobile. Copy this into your note to see how they're used:
```md
You can create footnote references that are short[^1] or long.[^2]
You can also create them inline.^[which may be easier,
since you don't need to pick an identifier and move down to type the note]
The footnotes are automatically numbered at the bottom of your note,
but you'll need to manually number your superscripts.
Make sure to count your variable[^variable] footnotes.[^5]
[^1]: Here's a footnote.
[^2]: Heres a footnote with multiple blocks.
Subsequent paragraphs are indented to show that they belong to the previous footnote.
{ eight spaces for some code }
The whole paragraph can be indented, or just the first
line. In this way, multi-paragraph footnotes work like
multi-paragraph list items.
This paragraph wont be part of the footnote, because it
isnt indented.
[^variable]: The variable footnote is the fourth footnote.
[^5]: This is the fifth footnote.
```
#### Not yet available:
- KaTeX
- Printing
- Custom Font Families
- Custom Font Sizes
- Superscript
- Subscript
## Further Resources
- [GitHub](https://github.com/sn-extensions/markdown-basic/) - Development instructions, license (AGPL-3.0-or-later), and source code.

View File

@@ -1,98 +0,0 @@
---
id: 'markdown-math'
title: Markdown Math
sidebar_label: Markdown Math
description: How to use the Standard Notes Markdown Math editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- markdown math
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
The Markdown Math editor (aka Math Editor) is a [derived editor](https://standardnotes.com/help/77/what-are-editors) for Standard Notes. It is derived from the [Upmath](https://github.com/parpalak/upmath.me) editor by [Roman Parpalak](https://github.com/parpalak), but uses [KaTeX](https://katex.org) for client-side rendering. Because the original Upmath editor and the Markdown Math editor render math using slightly different methods, some TeX libraries and their environments may be available in the Upmath editor but not in the Markdown Math editor. For a full list of functions supported by KaTeX, please see the [official KaTeX documentation](https://katex.org/docs/supported.html).
## Features
- $\LaTeX$ math rendering via [$\KaTeX$](https://katex.org)
- Markdown with side-by-side live rendering
- Option to view the HTML source of the rendered markdown
- Option to overwrite the note text with the contents of a text file on local storage
- Option to download the plain note text as a text file to save on local storage
- Option to download the HTML source of the rendered text as a text file to save on local storage
## Style Guide
| Result | Markdown |
| :----------------- | :------------------------------------------- |
| **Bold** | \*\*text\*\* or \_\_text\_\_ |
| _Emphasize_ | \*text\* or \_text\_ |
| ~~Strike-through~~ | \~\~text\~\~ |
| Link | [text]\(http://) |
| Image | ![text]\(http://) |
| `Inline Code` | \`code\` |
| Code Block | \`\`\`language <br></br>code <br></br>\`\`\` |
| Unordered List | \* item <br></br> - item <br></br> + item |
| Ordered List | 1. item |
| Blockquote | \> quote |
| H1 | # Heading |
| H2 | ## Heading |
| H3 | ### Heading |
| H4 | #### Heading |
| Section Breaks | `---` or `***` |
## Tables
Colons can be used to align columns.
Copy this into your editor to see what it creates:
```
| Tables | Are | Cool |
| ---------------- | :-----------: | --------: |
| col 2 is | centered | \$149 |
| col 3 is | right-aligned | \$4.17 |
| privacy is | neat | \$2.48 |
| rows don't need to |be pretty| what? |
| the last line is | unnecessary | really?
| one more | row | Yay! 😆
```
## $\KaTeX$
The Markdown Math editor requires double dollar signs. For example, `$$\int_0^\infty f(x)dx$$` or `$$\pi$$` should yield $$\int_0^\infty f(x)dx$$ and $$\pi$$.
To use Display Mode in the KaTeX, use double dollar signs on new lines. For example,
```latex
Text
$$
\int_0^\infty f(x)dx
$$
More Text
```
should yield:
Text
$$
\int_0^\infty f(x)dx
$$
More Text
### $\KaTeX$ Tables
Please see [here](https://katex.org/docs/supported.html) and [here](https://katex.org/docs/support_table.html) for tables of all the functions and symbols that $\KaTeX$ supports.
## Further Resources
- [GitHub](https://github.com/sn-extensions/math-editor) - Development instructions, license (AGPL-3.0-or-later), and source code.

View File

@@ -1,14 +0,0 @@
---
id: 'markdown-minimist'
title: Markdown Minimist Editor
sidebar_label: Markdown Minimist
description: How to use the Standard Notes Markdown Minimist editor.
- standard notes
- docs
- notes app
- end-to-end encryption
- markdown minimist
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -1,125 +0,0 @@
---
id: 'markdown-pro'
title: Markdown Pro
sidebar_label: Markdown Pro
description: How to use the Standard Notes Markdown Pro editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- markdown pro
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
The Markdown Pro editor (aka Advanced Markdown Editor) is a [derived editor](https://standardnotes.com/help/77/what-are-editors) for Standard Notes. It is derived from the [Easy Markdown Editor](https://github.com/Ionaru/easy-markdown-editor) which uses [Codemirror](https://github.com/codemirror/codemirror).
## Features
- Markdown with live side-by-side rendering
- Three views: Edit, Split, and Preview
- Keyboard Shortcuts
- Inline styling with HTML/CSS
## Keyboard Shortcuts
| Result | Shortcut |
| :------------------ | :--------------- |
| Toggle Preview | Ctrl/⌘ + P |
| Toggle Side-by-Side | Ctrl/⌘ + Alt + P |
## Style Guide
| Result | Markdown | Shortcut |
| :----------------- | :---------------------------------------- | :---------------------------------- |
| **Bold** | \*\*text\*\* or \_\_text\_\_ | Ctrl/⌘ + B |
| _Emphasize_ | \*text\* or \_text\_ | Ctrl/⌘ + I |
| ~~Strike-through~~ | \~text\~ or \~\~text\~\~ | ❔ |
| Link | [text]\(http://) | Ctrl/⌘ + K |
| Image | ![text]\(http://) | Ctrl/⌘ + Alt + I |
| `Inline Code` | \`code\` | ❔ |
| `Code Block` | \`\`\`code\`\`\` | Ctrl/⌘ + Alt + C or tab or 7 spaces |
| Unordered List | \* item <br></br> - item <br></br> + item | Ctrl/⌘ + L |
| Ordered List | 1. item | Ctrl/⌘ + Alt + L |
| Remove List | | Ctrl/⌘ + E |
| Blockquote | \> quote | Ctrl + ' or Ctrl + " |
| H1 | # Heading | Ctrl/⌘ + H |
| H2 | ## Heading | Ctrl/⌘ + H (×2) |
| H3 | ### Heading | Ctrl/⌘ + H (×3) |
### Lists
Enter a space in front of the asterisk or number to indent the list.
Copy this into your editor to see what it creates:
```
1. First ordered list item
2. Another item
* One space for unordered sub-list item
- One space for another sub-list item
* Press tab for sub-sub-list item
1. Two tabs for sub-sub-sub list item 😀
1. Actual numbers don't matter, just that it's a number
1. One space for ordered sub-list item
1. One space for another sub-list item
* Press Tab
1. One tab
* Two tabs. You got it! 👏
4. And another item. Success! 🎉
```
## Tables
Colons can be used to align columns.
Copy this into your editor to see what it creates:
```
| Tables | Are | Cool |
| ---------------- | :-----------: | --------: |
| col 2 is | centered | \$149 |
| col 3 is | right-aligned | \$4.17 |
| privacy is | neat | \$2.48 |
| rows don't need to |be pretty| what? |
| the last line is | unnecessary | really?
| one more | row | Yay! 😆
```
## Inline Styling
You can personalize the styling of the editor with inline HTML/CSS. For example, if you want to use monospace font for the editor, add this to your note, and replace `var(--sn-stylekit-monospace-font)` with your preferred font-families:
```html
<style>
.CodeMirror {
font-family: var(--sn-stylekit-monospace-font);
}
</style>
```
If you want to use monospace font for the preview, adjust the styles for `.editor-preview`:
```html
<style>
.CodeMirror,
.editor-preview {
font-family: var(--sn-stylekit-monospace-font);
}
</style>
```
#### Not yet available:
- Footnotes
- Superscript
- Subscript
- Syntax Highlighting
- Printing
- Default Custom Fonts
## Further Resources
- [GitHub](https://github.com/standardnotes/markdown-pro) - Development instructions, license (AGPL-3.0-or-later), and source code.

View File

@@ -1,17 +0,0 @@
---
id: 'plus-editor'
title: Plus Editor
sidebar_label: Plus Editor
description: How to use the Standard Notes Plus editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- plus editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Style Guide

View File

@@ -1,38 +0,0 @@
---
id: 'secure-spreadsheets'
title: Secure Spreadsheets Editor
sidebar_label: Secure Spreadsheets
description: How to use the Standard Notes secure spreadsheets editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
Secure Spreadsheets is a [derived editor](https://standardnotes.com/help/77/what-are-editors). It is derived from [Kendo UI Professional](https://github.com/telerik/kendo-ui-core#features-of-kendo-ui-core).
A demo of the Secure Spreadsheets editor is available at [standardnotes.com/demo](https://standardnotes.com/demo). To use Secure Spreadsheets with Standard Notes, please sign up for [Standard Notes Extended](https://standardnotes.com/plans) and install it by following the instructions described [here](https://standardnotes.com/help/29/how-do-i-install-extensions-once-i-ve-signed-up-for-extended).
## Export
You can export your spreadsheet as an `.xlsx` to use with Libre Office, Microsoft Office, Google Sheets, or other compatible program by clicking the download icon in the top left of the spreadsheets.
You can also export your spreadsheet as a `.pdf` file. The download menu has the following options:
- Export: Entire Workbook, Active Sheet, or Selection.
- Paper size: A2, A3, A4, A5, B3, B4, B5, Folio, Legal, Letter, Tabloid, and Executive.
- Margins: Normal and Wide.
- Orientation: Portrait or Landscape.
- With or without Guidelines (grid lines).
- Center: Horizontally, Vertically, Neither, or Both.
## License
The Secure Spreadsheets editor is derived from [Kendo UI Professional](https://github.com/telerik/kendo-ui-core#features-of-kendo-ui-core), which is not released as free and open-source software. To prevent vendor lock-in, you can easily [export your spreadsheet](#export) as an `.xlsx` or `.pdf` file.

View File

@@ -1,29 +0,0 @@
---
id: 'task-editor'
title: Simple Task Editor
sidebar_label: Task Editor
description: How to use the Standard Notes simple task editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- simple task editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Add Task
To add a task, enter the task into the box at the top then press enter/return.
## Rearrange Tasks
To rearrange tasks on the desktop or web apps, click on the task's checkbox then drag it up or down.
Rearranging tasks is currently unavailable on mobile.
## Delete Task
To delete a task, delete the task's text, then press enter/return. 😄

View File

@@ -1,168 +0,0 @@
---
title: Smart Views
sidebar_label: Smart Views
description: How to use the Standard Notes Smart Views.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- tags
- views
- filters
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Questions:
- How do I view a list of untagged notes (and create other dynamic filters)?
## Introduction
“Smart Views" are user-made dynamic folders that organize your notes according to predefined filters.
For example, suppose you wanted to see a list of all notes whose title starts with “Foo”. You can do this by creating a smart tag.
## Creating A Smart View
1. Create a new folder by clicking the + icon.
1. Copy and paste the following Smart View syntax, as the folder name:
```
!["Foo Notes", "title", "startsWith", "Foo"]]
```
1. Press enter on your keyboard.
At this point, you should see an item called "Foo notes" under **Views**. You can select this item to view a list of your notes that start with “Foo”.
## Understanding The Smart View Syntax
Smart Views can be used to construct any kind of simple query. The components of the smart tag syntax are as follows:
`!`: Indicates the start of a Smart View
`[...]`: A JSON array
- The first item in the JSON array is the display label.
- The second item is the note attribute you are targeting.
- The third is the comparison operator.
- And the last is the expected value.
## More Examples
Show all notes that have tags that start with the letter b:
```
!["B-tags", "tags", "includes", ["title", "startsWith", "b"]]
```
Show all notes that have tags `Blog` or `Ideas`:
```
!["Blog or Ideas", "tags", "includes", ["title", "in", ["Blog", "Ideas"]]]
```
Show notes that are pinned:
```
!["Pinned", "pinned", "=", true]
```
Show notes that are not pinned:
```
!["Not Pinned", "pinned", "=", false]
```
Show notes that have been updated within the last day:
```
!["Last Day", "updated_at", ">", "1.days.ago"]
```
Show notes whose text has more than 500 characters:
```
!["Long", "text.length", ">", 500]
```
### Compound Predicates
You can use compound and/or predicates to combine multiple queries. For example, to show all notes that are pinned and locked:
```
!["Pinned & Locked", "ignored", "and", [["pinned", "=", true], ["locked", "=", true]]]
```
Show all notes that are protected or pinned:
```
!["Protected or Pinned", "ignored", "or", [["content.protected", "=", true], ["pinned", "=", true]]]
```
Show all notes that have tags `Blog` or `Ideas`.
```
!["Blog Scheduled or Published", "ignored", "or", [["tags", "includes", ["title", "=", "Blog"]], ["tags", "includes", ["title", "=", "Ideas"]]]]
```
You can also use the not predicate to negate an expression. For example, to show all notes that do not have the `Unread` tag:
```
!["Read", "tags", "not", ["tags", "includes", ["title", "=", "Unread"]]]
```
The not predicate can be combined with the compound operators. For example, to show all notes that have the Blog tag but not the Ideas one:
```
!["Blog Unpublished", "ignored", "and", [["tags", "includes", ["title", "=", "Blog"]], ["", "not", ["tags", "includes", ["title", "=", "Ideas"]]]]]
```
## Attributes
Here are a list of note attributes that can be queried:
- `title`
- `title.length`
- `text`
- `text.length`
- `tags`
- `updated_at`
- `created_at`
- `pinned`
- `content.protected`
If constructing a filter that queries tags, you can use the following tag attributes:
- `title`
- `title.length`
- `updated_at`
- `created_at`
Note that Smart Views always query notes, and so the query you're building refers to notes firstmost. You reference tags by referring to a note's tags:
```
!["B-tags", "tags", "includes", ["title", "startsWith", "b"]]
```
Get all notes whose tags includes a title that starts with the letter b.
## Operators
Here are a list of operators that can be used to construct filters. The operator is typically the third parameter in the filter syntax.
- `=`
- `>`
- `<`
- `>=`
- `<=`
- `startsWith`
- `in` ("whether a value is in a list of values")
- `includes` ("includes sub filter")
- `matches` (regex pattern)
- `and` (for compound filters)
- `or` (for compound filters)
- `not` (negates the expected value, attribute is ignored)

View File

@@ -1,15 +0,0 @@
---
id: 'token-vault'
title: Token Vault Editor
sidebar_label: Token Vault
description: How to use the Standard Notes Token Vault extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -1,15 +0,0 @@
---
id: 'vim-editor'
title: Vim Editor
sidebar_label: Vim Editor
description: How to use the Standard Notes vim editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- vim editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -1,36 +0,0 @@
---
id: welcome
slug: /
title: 'Welcome to the Standard Notes Documentation'
sidebar_label: Welcome
description: Welcome to the Standard Notes Documentation
keywords:
- standard notes
- notes app
- end-to-end encryption
- welcome
image: /img/logo.png
hide_title: true
hide_table_of_contents: false
---
## Welcome to the Standard Notes Documentation
This documentation explains how to get started with using Standard Notes, publishing a blog with Listed, self-hosting a Standard Notes syncing server, developing an extension to work with Standard Notes, and developing a client application for Standard Notes (e.g., a command line application).
## Topics
- [Self-hosting a Syncing Server](/self-hosting/getting-started/)
- [Developing Extensions](/extensions/intro/)
- [Client Encryption API](/specification/encryption/)
## Staying informed
- [GitHub](https://github.com/standardnotes)
- [Discord](https://standardnotes.com/discord)
- [Twitter](https://twitter.com/standardnotes)
- [Blog](https://standardnotes.com/blog)
## Something missing?
If you find issues with the documentation or have suggestions on how to improve the documentation or the project in general, please [file an issue](https://github.com/standardnotes/forum) on the forum or send a tweet mentioning [@standardnotes](https://twitter.com/standardnotes).

View File

@@ -1,212 +0,0 @@
const math = require('remark-math');
const katex = require('rehype-katex');
module.exports = {
title: 'Standard Notes Documentation',
tagline: 'Extend Your Notes App',
url: 'https://docs.standardnotes.com',
baseUrl: '/',
favicon: 'img/favicon.png',
organizationName: 'standardnotes',
projectName: 'docs',
themeConfig: {
algolia: {
apiKey: 'f2899fea0369aeea336963e48a0e46dc',
indexName: 'standardnotes',
},
colorMode: {
defaultMode: 'light',
disableSwitch: false,
respectPrefersColorScheme: true,
},
hideableSidebar: true,
image: 'img/logo.png',
navbar: {
hideOnScroll: true,
title: 'Standard Notes',
logo: {
alt: 'Standard Notes Logo',
src: 'img/logo.png',
},
items: [
{
label: 'Developers',
position: 'left',
items: [
{
to: '/self-hosting/getting-started',
label: 'Self-Host a Sync Server',
position: 'left',
},
{
to: '/specification/encryption',
label: 'Client Encryption API',
position: 'left',
},
{
to: '/extensions/intro',
label: 'Build an Extension',
position: 'left',
},
{
to: '/extensions/editors',
label: 'Build an Editor',
position: 'left',
},
],
},
{
href: 'https://app.standardnotes.com',
label: 'Standard Notes Web App',
position: 'right',
},
{
href: 'https://standardnotes.com',
label: 'Our Website — Encrypted Notes App',
position: 'right',
},
{
href: 'https://standardnotes.com/help',
label: 'Help',
position: 'right',
},
{
href: 'https://standardnotes.com/blog',
label: 'Privacy & Security Blog',
position: 'right',
},
{
href: 'https://github.com/standardnotes',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
links: [
{
title: 'Developers',
items: [
{
label: 'Self-Host a Sync Server',
to: '/self-hosting/getting-started',
},
{
label: 'Build an Extension',
to: '/extensions/intro',
},
{
label: 'Encryption Specification',
to: '/specification/encryption',
},
],
},
{
title: 'Community',
items: [
{
label: 'Join our Discord',
href: 'https://standardnotes.com/discord',
},
{
label: 'Community Forum',
href: 'https://standardnotes.com/forum',
},
{
label: 'Listed Blogging Platform',
href: 'https://listed.to',
},
],
},
{
title: 'Support',
items: [
{
label: 'Help & Contact',
href: 'https://standardnotes.com/help',
},
{
label: 'Twitter',
href: 'https://twitter.com/standardnotes',
},
{
label: 'Reddit',
href: 'https://reddit.com/r/standardnotes',
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} <a href="https://standardnotes.com">Standard Notes</a>`,
},
},
stylesheets: [
{
href: '/katex/v0.12.0/katex.min.css',
type: 'text/css',
integrity:
'sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X',
crossorigin: 'anonymous',
},
],
scripts: [
{
src: 'https://plausible.standardnotes.com/js/plausible.js',
'data-domain': 'docs.standardnotes.com',
defer: true,
},
],
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
editUrl:
'https://github.com/standardnotes/app/blob/main/packages/docs/',
routeBasePath: '/',
remarkPlugins: [math],
rehypePlugins: [katex],
showLastUpdateTime: true,
},
theme: {
customCss: require.resolve('./src/css/custom.scss'),
},
},
],
],
plugins: [
[
'@docusaurus/plugin-client-redirects',
{
redirects: [
{
to: '/extensions/intro',
from: [
'/extensions/introduction',
'/extensions/components',
'/extensions',
'/extensions/intro-to-extensions',
],
},
{
to: '/usage/filesafe/aws',
from: ['/filesafe/aws'],
},
{
to: '/self-hosting/getting-started',
from: [
'/self-hosting',
'/self-hosting/getting-started-with-self-hosting',
],
},
{
to: '/specification/encryption',
from: ['/specification/'],
},
],
},
],
'docusaurus-plugin-sass',
],
};

View File

@@ -1,47 +0,0 @@
{
"name": "@standardnotes/docs",
"version": "0.2.9",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start --port=3002",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"dev": "docusaurus start",
"pretty": "prettier --write 'src/**/*.{html,css,js,jsx,json}' 'docs/**/*.{md,mdx,js,jsx}' sidebars.js docusaurus.config.js"
},
"installConfig": {
"hoistingLimits": "workspaces"
},
"dependencies": {
"@docusaurus/core": "2.0.0-alpha.73",
"@docusaurus/plugin-client-redirects": "^2.0.0-alpha.73",
"@docusaurus/preset-classic": "2.0.0-alpha.73",
"clsx": "^1.1.1",
"docusaurus-plugin-sass": "^0.1.12",
"prettier": "^2.2.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rehype-katex": "^4.0.0",
"remark-math": "^3.0.1"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View File

@@ -1,54 +0,0 @@
module.exports = {
mainSidebar: {
Developers: [
{
'Self Hosting': [
'self-hosting/getting-started',
'self-hosting/docker',
'self-hosting/configuration-options',
'self-hosting/legacy-migration',
'self-hosting/https-support',
'self-hosting/updating',
'self-hosting/subscriptions',
'self-hosting/file-uploads',
],
},
{
API: ['specification/encryption'],
},
{
Extensions: [
'extensions/intro',
{
type: 'category',
label: 'Editors',
items: [
'extensions/editors',
'extensions/editors-getting-started',
'extensions/editorkit',
'extensions/stylekit',
],
},
'extensions/local-setup',
'extensions/themes',
'extensions/actions',
'extensions/publishing',
],
},
],
Editors: [
'usage/bold-editor',
'usage/markdown-basic',
'usage/markdown-math',
'usage/markdown-pro',
'usage/secure-spreadsheets',
'usage/task-editor',
],
},
secondSidebar: {
Troubleshooting: [
'troubleshooting/reset-apps',
'troubleshooting/import-backups',
],
},
};

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Head from '@docusaurus/Head';
const CanonicalUrl = ({ canonicalUrl }) => (
<Head>
<link rel="canonical" href={canonicalUrl} />
</Head>
);
export default CanonicalUrl;

View File

@@ -1,115 +0,0 @@
/**
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #086dd6;
--ifm-color-primary-dark: #0762c1;
--ifm-color-primary-darker: #075db6;
--ifm-color-primary-darkest: #064c96;
--ifm-color-primary-light: #0978eb;
--ifm-color-primary-lighter: #097df6;
--ifm-color-primary-lightest: #2a8ef7;
--ifm-code-font-size: 90%;
}
.docusaurus-highlight-code-line {
background-color: rgb(72, 77, 91);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}
/* Adjust dark theme colors */
html[data-theme='dark'] {
--ifm-background-color: #20232a;
}
/* Adjust font sizes */
@media screen and (max-width: 996px) {
:root {
--ifm-font-size-base: 18px;
}
}
@media screen and (min-width: 997px) {
:root {
--ifm-font-size-base: 17px;
}
}
article header h1[class^='docTitle'] {
font-size: 2rem;
}
// Don't make headers so big
.markdown > h2,
h3,
h4,
h5,
h6 {
--ifm-h2-font-size: inherit !important;
--ifm-h3-font-size: inherit !important;
--ifm-h4-font-size: inherit !important;
--ifm-h5-font-size: inherit !important;
--ifm-h6-font-size: inherit !important;
}
/* Announcement Bar */
div[class^='announcementBar'],
button[class^='announcementBar'] {
background-color: var(--ifm-background-surface-color);
color: var(--ifm-font-color-base);
}
div[class^='announcementBar'] a {
color: var(--ifm-link-color);
text-decoration: inherit;
}
div[class^='announcementBar'] a:hover {
color: var(--ifm-link-color);
text-decoration: underline;
}
/* Adjust navbar width */
.navbar .navbar__inner {
max-width: 1360px;
margin: 0 auto;
}
/* Header Dropdown Menu */
.dropdown__link {
font-size: inherit;
}
/* Remove shadow on the left */
:root {
--ifm-global-shadow-md: 0px;
}
/* Adjust main wrapper width */
@media (min-width: 1416px) {
.main-wrapper {
max-width: 1400px;
width: 1400px;
align-self: center;
}
}
/* Footer */
.footer {
.container {
max-width: 900px;
}
}
#__docusaurus > footer > div > div.text--center > div > a {
color: unset;
}
#__docusaurus > footer > div > div.text--center > div > a:hover {
color: var(--ifm-link-color);
text-decoration: underline;
}

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/self-hosting/docker" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/about" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/options/#separate-pages" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/styles" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/subscribers" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/options/#hide-post" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/settings" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/publishing" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/register" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/options/#custom-dates" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/about" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/listed/guestbook" />;
}
export default Hello;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import Layout from '@theme/Layout';
import { Redirect } from '@docusaurus/router';
function Hello() {
return <Redirect to="/self-hosting/docker" />;
}
export default Hello;

View File

@@ -1,35 +0,0 @@
/**
* CSS files with the .module.css suffix will be treated as CSS modules
* and scoped locally.
*/
.heroBanner {
padding: 4rem 0;
text-align: center;
position: relative;
overflow: hidden;
}
@media screen and (max-width: 966px) {
.heroBanner {
padding: 2rem;
}
}
.buttons {
display: flex;
align-items: center;
justify-content: center;
}
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureImage {
height: 200px;
width: 200px;
}

View File

View File

@@ -1 +0,0 @@
docs.standardnotes.com

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,91 +0,0 @@
# [<img src="https://katex.org/img/katex-logo-black.svg" width="130" alt="KaTeX">](https://katex.org/)
[![npm](https://img.shields.io/npm/v/katex.svg)](https://www.npmjs.com/package/katex)
[![CircleCI](https://circleci.com/gh/KaTeX/KaTeX.svg?style=shield)](https://circleci.com/gh/KaTeX/KaTeX)
[![codecov](https://codecov.io/gh/KaTeX/KaTeX/branch/master/graph/badge.svg)](https://codecov.io/gh/KaTeX/KaTeX)
[![Join the chat at https://gitter.im/KaTeX/KaTeX](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/KaTeX/KaTeX?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=KaTeX/KaTeX)](https://dependabot.com)
[![jsDelivr](https://data.jsdelivr.com/v1/package/npm/katex/badge?style=rounded)](https://www.jsdelivr.com/package/npm/katex)
![](https://img.badgesize.io/KaTeX/KaTeX/v0.11.1/dist/katex.min.js?compression=gzip)
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](http://www.intmath.com/cg5/katex-mathjax-comparison.php).
* **Print quality:** KaTeX's layout is based on Donald Knuth's TeX, the gold standard for math typesetting.
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
KaTeX is compatible with all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 911.
KaTeX supports much (but not all) of LaTeX and many LaTeX packages. See the [list of supported functions](https://katex.org/docs/supported.html).
Try out KaTeX [on the demo page](https://katex.org/#demo)!
## Getting started
### Starter template
```html
<!DOCTYPE html>
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">
<!-- The loading of KaTeX is deferred to speed up page rendering -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js" integrity="sha384-y23I5Q6l+B6vatafAwxRu/0oK/79VlbSz7Q9aiSZUvyWYIYsd+qj+o24G5ZU2zJz" crossorigin="anonymous"></script>
<!-- To automatically render math in text elements, include the auto-render extension: -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/contrib/auto-render.min.js" integrity="sha384-kWPLUVMOks5AQFrykwIup5lo0m3iMkkHrD0uJ4H5cjeGihAutqP0yW0J6dpFiVkI" crossorigin="anonymous"
onload="renderMathInElement(document.body);"></script>
</head>
...
</html>
```
You can also [download KaTeX](https://github.com/KaTeX/KaTeX/releases) and host it yourself.
For details on how to configure auto-render extension, refer to [the documentation](https://katex.org/docs/autorender.html).
### API
Call `katex.render` to render a TeX expression directly into a DOM element.
For example:
```js
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, {
throwOnError: false
});
```
Call `katex.renderToString` to generate an HTML string of the rendered math,
e.g., for server-side rendering. For example:
```js
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}", {
throwOnError: false
});
// '<span class="katex">...</span>'
```
Make sure to include the CSS and font files in both cases.
If you are doing all rendering on the server, there is no need to include the
JavaScript on the client.
The examples above use the `throwOnError: false` option, which renders invalid
inputs as the TeX source code in red (by default), with the error message as
hover text. For other available options, see the
[API documentation](https://katex.org/docs/api.html),
[options documentation](https://katex.org/docs/options.html), and
[handling errors documentation](https://katex.org/docs/error.html).
## Demo and Documentation
Learn more about using KaTeX [on the website](https://katex.org)!
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)
## License
KaTeX is licensed under the [MIT License](http://opensource.org/licenses/MIT).

View File

@@ -1,339 +0,0 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("katex"));
else if(typeof define === 'function' && define.amd)
define(["katex"], factory);
else if(typeof exports === 'object')
exports["renderMathInElement"] = factory(require("katex"));
else
root["renderMathInElement"] = factory(root["katex"]);
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__0__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE__0__;
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
// EXTERNAL MODULE: external "katex"
var external_katex_ = __webpack_require__(0);
var external_katex_default = /*#__PURE__*/__webpack_require__.n(external_katex_);
// CONCATENATED MODULE: ./contrib/auto-render/splitAtDelimiters.js
/* eslint no-constant-condition:0 */
var findEndOfMath = function findEndOfMath(delimiter, text, startIndex) {
// Adapted from
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
var index = startIndex;
var braceLevel = 0;
var delimLength = delimiter.length;
while (index < text.length) {
var character = text[index];
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
return index;
} else if (character === "\\") {
index++;
} else if (character === "{") {
braceLevel++;
} else if (character === "}") {
braceLevel--;
}
index++;
}
return -1;
};
var splitAtDelimiters = function splitAtDelimiters(startData, leftDelim, rightDelim, display) {
var finalData = [];
for (var i = 0; i < startData.length; i++) {
if (startData[i].type === "text") {
var text = startData[i].data;
var lookingForLeft = true;
var currIndex = 0;
var nextIndex = void 0;
nextIndex = text.indexOf(leftDelim);
if (nextIndex !== -1) {
currIndex = nextIndex;
finalData.push({
type: "text",
data: text.slice(0, currIndex)
});
lookingForLeft = false;
}
while (true) {
if (lookingForLeft) {
nextIndex = text.indexOf(leftDelim, currIndex);
if (nextIndex === -1) {
break;
}
finalData.push({
type: "text",
data: text.slice(currIndex, nextIndex)
});
currIndex = nextIndex;
} else {
nextIndex = findEndOfMath(rightDelim, text, currIndex + leftDelim.length);
if (nextIndex === -1) {
break;
}
finalData.push({
type: "math",
data: text.slice(currIndex + leftDelim.length, nextIndex),
rawData: text.slice(currIndex, nextIndex + rightDelim.length),
display: display
});
currIndex = nextIndex + rightDelim.length;
}
lookingForLeft = !lookingForLeft;
}
finalData.push({
type: "text",
data: text.slice(currIndex)
});
} else {
finalData.push(startData[i]);
}
}
return finalData;
};
/* harmony default export */ var auto_render_splitAtDelimiters = (splitAtDelimiters);
// CONCATENATED MODULE: ./contrib/auto-render/auto-render.js
/* eslint no-console:0 */
var auto_render_splitWithDelimiters = function splitWithDelimiters(text, delimiters) {
var data = [{
type: "text",
data: text
}];
for (var i = 0; i < delimiters.length; i++) {
var delimiter = delimiters[i];
data = auto_render_splitAtDelimiters(data, delimiter.left, delimiter.right, delimiter.display || false);
}
return data;
};
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
* API, we should copy it before mutating.
*/
var auto_render_renderMathInText = function renderMathInText(text, optionsCopy) {
var data = auto_render_splitWithDelimiters(text, optionsCopy.delimiters);
var fragment = document.createDocumentFragment();
for (var i = 0; i < data.length; i++) {
if (data[i].type === "text") {
fragment.appendChild(document.createTextNode(data[i].data));
} else {
var span = document.createElement("span");
var math = data[i].data; // Override any display mode defined in the settings with that
// defined by the text itself
optionsCopy.displayMode = data[i].display;
try {
if (optionsCopy.preProcess) {
math = optionsCopy.preProcess(math);
}
external_katex_default.a.render(math, span, optionsCopy);
} catch (e) {
if (!(e instanceof external_katex_default.a.ParseError)) {
throw e;
}
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
fragment.appendChild(document.createTextNode(data[i].rawData));
continue;
}
fragment.appendChild(span);
}
}
return fragment;
};
var renderElem = function renderElem(elem, optionsCopy) {
for (var i = 0; i < elem.childNodes.length; i++) {
var childNode = elem.childNodes[i];
if (childNode.nodeType === 3) {
// Text node
var frag = auto_render_renderMathInText(childNode.textContent, optionsCopy);
i += frag.childNodes.length - 1;
elem.replaceChild(frag, childNode);
} else if (childNode.nodeType === 1) {
(function () {
// Element node
var className = ' ' + childNode.className + ' ';
var shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(function (x) {
return className.indexOf(' ' + x + ' ') === -1;
});
if (shouldRender) {
renderElem(childNode, optionsCopy);
}
})();
} // Otherwise, it's something else, and ignore it.
}
};
var renderMathInElement = function renderMathInElement(elem, options) {
if (!elem) {
throw new Error("No element provided to render");
}
var optionsCopy = {}; // Object.assign(optionsCopy, option)
for (var option in options) {
if (options.hasOwnProperty(option)) {
optionsCopy[option] = options[option];
}
} // default options
optionsCopy.delimiters = optionsCopy.delimiters || [{
left: "$$",
right: "$$",
display: true
}, {
left: "\\(",
right: "\\)",
display: false
}, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
// {left: "$", right: "$", display: false},
// \[…\] must come last in this array. Otherwise, renderMathInElement
// will search for \[ before it searches for $$ or \(
// That makes it susceptible to finding a \\[0.3em] row delimiter and
// treating it as if it were the start of a KaTeX math zone.
{
left: "\\[",
right: "\\]",
display: true
}];
optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code"];
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
// math elements within a single call to `renderMathInElement`.
optionsCopy.macros = optionsCopy.macros || {};
renderElem(elem, optionsCopy);
};
/* harmony default export */ var auto_render = __webpack_exports__["default"] = (renderMathInElement);
/***/ })
/******/ ])["default"];
});

View File

@@ -1 +0,0 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,function(e){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=1)}([function(t,r){t.exports=e},function(e,t,r){"use strict";r.r(t);var n=r(0),o=r.n(n),a=function(e,t,r){for(var n=r,o=0,a=e.length;n<t.length;){var i=t[n];if(o<=0&&t.slice(n,n+a)===e)return n;"\\"===i?n++:"{"===i?o++:"}"===i&&o--,n++}return-1},i=function(e,t,r,n){for(var o=[],i=0;i<e.length;i++)if("text"===e[i].type){var l=e[i].data,d=!0,s=0,f=void 0;for(-1!==(f=l.indexOf(t))&&(s=f,o.push({type:"text",data:l.slice(0,s)}),d=!1);;){if(d){if(-1===(f=l.indexOf(t,s)))break;o.push({type:"text",data:l.slice(s,f)}),s=f}else{if(-1===(f=a(r,l,s+t.length)))break;o.push({type:"math",data:l.slice(s+t.length,f),rawData:l.slice(s,f+r.length),display:n}),s=f+r.length}d=!d}o.push({type:"text",data:l.slice(s)})}else o.push(e[i]);return o},l=function(e,t){for(var r=function(e,t){for(var r=[{type:"text",data:e}],n=0;n<t.length;n++){var o=t[n];r=i(r,o.left,o.right,o.display||!1)}return r}(e,t.delimiters),n=document.createDocumentFragment(),a=0;a<r.length;a++)if("text"===r[a].type)n.appendChild(document.createTextNode(r[a].data));else{var l=document.createElement("span"),d=r[a].data;t.displayMode=r[a].display;try{t.preProcess&&(d=t.preProcess(d)),o.a.render(d,l,t)}catch(e){if(!(e instanceof o.a.ParseError))throw e;t.errorCallback("KaTeX auto-render: Failed to parse `"+r[a].data+"` with ",e),n.appendChild(document.createTextNode(r[a].rawData));continue}n.appendChild(l)}return n};t.default=function(e,t){if(!e)throw new Error("No element provided to render");var r={};for(var n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);r.delimiters=r.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\[",right:"\\]",display:!0}],r.ignoredTags=r.ignoredTags||["script","noscript","style","textarea","pre","code"],r.ignoredClasses=r.ignoredClasses||[],r.errorCallback=r.errorCallback||console.error,r.macros=r.macros||{},function e(t,r){for(var n=0;n<t.childNodes.length;n++){var o=t.childNodes[n];if(3===o.nodeType){var a=l(o.textContent,r);n+=a.childNodes.length-1,t.replaceChild(a,o)}else 1===o.nodeType&&function(){var t=" "+o.className+" ";-1===r.ignoredTags.indexOf(o.nodeName.toLowerCase())&&r.ignoredClasses.every(function(e){return-1===t.indexOf(" "+e+" ")})&&e(o,r)}()}}(e,r)}}]).default});

View File

@@ -1,215 +0,0 @@
import katex from '../katex.mjs';
/* eslint no-constant-condition:0 */
const findEndOfMath = function findEndOfMath(delimiter, text, startIndex) {
// Adapted from
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
let index = startIndex;
let braceLevel = 0;
const delimLength = delimiter.length;
while (index < text.length) {
const character = text[index];
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
return index;
} else if (character === "\\") {
index++;
} else if (character === "{") {
braceLevel++;
} else if (character === "}") {
braceLevel--;
}
index++;
}
return -1;
};
const splitAtDelimiters = function splitAtDelimiters(startData, leftDelim, rightDelim, display) {
const finalData = [];
for (let i = 0; i < startData.length; i++) {
if (startData[i].type === "text") {
const text = startData[i].data;
let lookingForLeft = true;
let currIndex = 0;
let nextIndex;
nextIndex = text.indexOf(leftDelim);
if (nextIndex !== -1) {
currIndex = nextIndex;
finalData.push({
type: "text",
data: text.slice(0, currIndex)
});
lookingForLeft = false;
}
while (true) {
if (lookingForLeft) {
nextIndex = text.indexOf(leftDelim, currIndex);
if (nextIndex === -1) {
break;
}
finalData.push({
type: "text",
data: text.slice(currIndex, nextIndex)
});
currIndex = nextIndex;
} else {
nextIndex = findEndOfMath(rightDelim, text, currIndex + leftDelim.length);
if (nextIndex === -1) {
break;
}
finalData.push({
type: "math",
data: text.slice(currIndex + leftDelim.length, nextIndex),
rawData: text.slice(currIndex, nextIndex + rightDelim.length),
display: display
});
currIndex = nextIndex + rightDelim.length;
}
lookingForLeft = !lookingForLeft;
}
finalData.push({
type: "text",
data: text.slice(currIndex)
});
} else {
finalData.push(startData[i]);
}
}
return finalData;
};
/* eslint no-console:0 */
const splitWithDelimiters = function splitWithDelimiters(text, delimiters) {
let data = [{
type: "text",
data: text
}];
for (let i = 0; i < delimiters.length; i++) {
const delimiter = delimiters[i];
data = splitAtDelimiters(data, delimiter.left, delimiter.right, delimiter.display || false);
}
return data;
};
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
* API, we should copy it before mutating.
*/
const renderMathInText = function renderMathInText(text, optionsCopy) {
const data = splitWithDelimiters(text, optionsCopy.delimiters);
const fragment = document.createDocumentFragment();
for (let i = 0; i < data.length; i++) {
if (data[i].type === "text") {
fragment.appendChild(document.createTextNode(data[i].data));
} else {
const span = document.createElement("span");
let math = data[i].data; // Override any display mode defined in the settings with that
// defined by the text itself
optionsCopy.displayMode = data[i].display;
try {
if (optionsCopy.preProcess) {
math = optionsCopy.preProcess(math);
}
katex.render(math, span, optionsCopy);
} catch (e) {
if (!(e instanceof katex.ParseError)) {
throw e;
}
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
fragment.appendChild(document.createTextNode(data[i].rawData));
continue;
}
fragment.appendChild(span);
}
}
return fragment;
};
const renderElem = function renderElem(elem, optionsCopy) {
for (let i = 0; i < elem.childNodes.length; i++) {
const childNode = elem.childNodes[i];
if (childNode.nodeType === 3) {
// Text node
const frag = renderMathInText(childNode.textContent, optionsCopy);
i += frag.childNodes.length - 1;
elem.replaceChild(frag, childNode);
} else if (childNode.nodeType === 1) {
// Element node
const className = ' ' + childNode.className + ' ';
const shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(x => className.indexOf(' ' + x + ' ') === -1);
if (shouldRender) {
renderElem(childNode, optionsCopy);
}
} // Otherwise, it's something else, and ignore it.
}
};
const renderMathInElement = function renderMathInElement(elem, options) {
if (!elem) {
throw new Error("No element provided to render");
}
const optionsCopy = {}; // Object.assign(optionsCopy, option)
for (const option in options) {
if (options.hasOwnProperty(option)) {
optionsCopy[option] = options[option];
}
} // default options
optionsCopy.delimiters = optionsCopy.delimiters || [{
left: "$$",
right: "$$",
display: true
}, {
left: "\\(",
right: "\\)",
display: false
}, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
// {left: "$", right: "$", display: false},
// \[…\] must come last in this array. Otherwise, renderMathInElement
// will search for \[ before it searches for $$ or \(
// That makes it susceptible to finding a \\[0.3em] row delimiter and
// treating it as if it were the start of a KaTeX math zone.
{
left: "\\[",
right: "\\]",
display: true
}];
optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code"];
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
// math elements within a single call to `renderMathInElement`.
optionsCopy.macros = optionsCopy.macros || {};
renderElem(elem, optionsCopy);
};
export default renderMathInElement;

View File

@@ -1,14 +0,0 @@
/* Force selection of entire .katex/.katex-display blocks, so that we can
* copy/paste the entire source code. If you omit this CSS, partial
* selections of a formula will work, but will copy the ugly HTML
* representation instead of the LaTeX source code. (Full selections will
* still produce the LaTeX source code.)
*/
.katex,
.katex-display {
user-select: all;
-moz-user-select: all;
-webkit-user-select: all;
-ms-user-select: all;
}

View File

@@ -1,213 +0,0 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})((typeof self !== 'undefined' ? self : this), function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
// EXTERNAL MODULE: ./contrib/copy-tex/copy-tex.css
var copy_tex = __webpack_require__(0);
// CONCATENATED MODULE: ./contrib/copy-tex/katex2tex.js
// Set these to how you want inline and display math to be delimited.
var defaultCopyDelimiters = {
inline: ['$', '$'],
// alternative: ['\(', '\)']
display: ['$$', '$$'] // alternative: ['\[', '\]']
}; // Replace .katex elements with their TeX source (<annotation> element).
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
// as in copy-tex.js.
var katexReplaceWithTex = function katexReplaceWithTex(fragment, copyDelimiters) {
if (copyDelimiters === void 0) {
copyDelimiters = defaultCopyDelimiters;
}
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
// (which will get replaced below).
var katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
for (var i = 0; i < katexHtml.length; i++) {
var element = katexHtml[i];
if (element.remove) {
element.remove(null);
} else {
element.parentNode.removeChild(element);
}
} // Replace .katex-mathml elements with their annotation (TeX source)
// descendant, with inline delimiters.
var katexMathml = fragment.querySelectorAll('.katex-mathml');
for (var _i = 0; _i < katexMathml.length; _i++) {
var _element = katexMathml[_i];
var texSource = _element.querySelector('annotation');
if (texSource) {
if (_element.replaceWith) {
_element.replaceWith(texSource);
} else {
_element.parentNode.replaceChild(texSource, _element);
}
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
}
} // Switch display math to display delimiters.
var displays = fragment.querySelectorAll('.katex-display annotation');
for (var _i2 = 0; _i2 < displays.length; _i2++) {
var _element2 = displays[_i2];
_element2.innerHTML = copyDelimiters.display[0] + _element2.innerHTML.substr(copyDelimiters.inline[0].length, _element2.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
}
return fragment;
};
/* harmony default export */ var katex2tex = (katexReplaceWithTex);
// CONCATENATED MODULE: ./contrib/copy-tex/copy-tex.js
// Global copy handler to modify behavior on .katex elements.
document.addEventListener('copy', function (event) {
var selection = window.getSelection();
if (selection.isCollapsed) {
return; // default action OK if selection is empty
}
var fragment = selection.getRangeAt(0).cloneContents();
if (!fragment.querySelector('.katex-mathml')) {
return; // default action OK if no .katex-mathml elements
} // Preserve usual HTML copy/paste behavior.
var html = [];
for (var i = 0; i < fragment.childNodes.length; i++) {
html.push(fragment.childNodes[i].outerHTML);
}
event.clipboardData.setData('text/html', html.join('')); // Rewrite plain-text version.
event.clipboardData.setData('text/plain', katex2tex(fragment).textContent); // Prevent normal copy handling.
event.preventDefault();
});
// CONCATENATED MODULE: ./contrib/copy-tex/copy-tex.webpack.js
/**
* This is the webpack entry point for KaTeX. As ECMAScript doesn't support
* CSS modules natively, a separate entry point is used.
*/
/***/ })
/******/ ])["default"];
});

View File

@@ -1 +0,0 @@
.katex,.katex-display{user-select:all;-moz-user-select:all;-webkit-user-select:all;-ms-user-select:all}

Some files were not shown because too many files have changed in this diff Show More