diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..189ee50 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,682 @@ +Pardis source code +Copyright (C) 2026 EI Adrien Weiskopff pardis@clawd.fr + +This program is licensed under the GNU General Public License, version 3 only. +The full license text follows. + +--- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/PRODUCTION.md b/PRODUCTION.md deleted file mode 100644 index 2ba98bd..0000000 --- a/PRODUCTION.md +++ /dev/null @@ -1,132 +0,0 @@ -# Validation avant production — Pardis - -## Vérifications exécutées dans cet environnement - -- Dernière validation locale : 10 octobre 2026, avec `npm run validate` et la - suite Playwright utilisant le Chromium Headless Shell installé via - `PARDIS_CHROME_PATH`. Ces résultats ne remplacent pas une recette sur téléphones - réels ni le résultat du workflow CI du commit publié. -- 272 tests unitaires et IndexedDB simulée passent (15 fichiers). -- `npm run check` : aucune erreur ni avertissement. -- Build PWA complet : `sw.js`, Workbox, manifeste et précache générés. -- L’importeur de la version précédente, extrait du commit avant l’évolution du format, refuse réellement - un export v2 avec son message de mise à jour (sans importer les pauses comme travail). -- Cas couverts : 8h30 de travail / 30 minutes de pause, récapitulatif/copier et - suggestions sans pause, reprise continue et discontinue à 18h, reprise avec - dernier repas/pause, deux appels concurrents, aucune pause vide, refus de reprise - passée, annulation transactionnelle sur erreur, repas automatique pendant une - pause, clôture pendant une pause, ajout/correction/suppression sur journée close, - import v1 et aller-retour v2, préservation des intervalles vides, écriture de - réglages contre leur dernière valeur persistée et échec d’écriture. - -Commande reproductible : `npm ci` puis `npm run validate`. -La CI Gitea exécute déjà tests, vérification Svelte et build avant déploiement. - -## Parcours navigateur automatisés - -Seize tests Playwright exécutent le build PWA réel dans Chrome, avec des profils -éphémères et des fixtures synthétiques à 320 px. Ils couvrent consultation et -correction, copie, statistiques, date et Retour après rechargement, focus et -Échap (y compris les panneaux imbriqués), annulation et double appui, deux onglets, -les reprises après repas/pause, export téléchargé puis vérifié, purge/restauration, -erreur IndexedDB puis Réessayer, erreur de réglage avec champ restauré, ajout -d’une pause dans un intervalle vide et rechargement hors ligne sous service worker. -L’écran **Données et sauvegardes** est vérifié depuis les réglages et le rappel, -avec Retour, rechargement, durée persistée et absence de débordement à 320 px. -Le bandeau de sauvegarde est aussi testé avec une police agrandie. - -Les trois parcours d’erreur ajoutés couvrent : - -- Échec d’écriture de la conservation : erreur visible, champ restauré, - base inchangée, valeur conservée après rechargement et nouvelle tentative réussie. -- Export JSON valide mais modifié : refus de vérification sans changement des - journées, postes, réglages ou statut de sauvegarde ; le fichier original peut - ensuite être vérifié. -- Échec de suppression des journées après écriture de la sauvegarde et suppression - des postes : annulation de toute la transaction, y compris restauration de la - sauvegarde précédente ; le dialogue reste ouvert et permet de réessayer. - -Les captures MCP avaient été refusées par le contrôle d’autorisation. Les tests -Playwright ont été autorisés comme vérification automatisée isolée : aucune trace, -vidéo ou capture n’est produite, aucun contenu de page n’est transmis à MCP. - -Commandes : `npx playwright install chromium` puis `npm run validate:all`. -Un navigateur installé peut être choisi avec `PARDIS_CHROME_PATH`. -La CI installe Chromium avec ses dépendances et lance les mêmes parcours. -`docker compose config --quiet` a été validé avec des variables d’exemple. -Cette sandbox ne pouvait pas lancer les conteneurs (socket Docker indisponible), -mais l'opérateur confirme que le déploiement Docker de production et son job -CI/CD fonctionnent et sont en service depuis plusieurs commits. La VM qui héberge -l'instance reçoit une sauvegarde quotidienne. Celle-ci protège l'hébergement et -le service de synchronisation, mais pas les données locales conservées dans les -navigateurs des utilisateurs. - -## Vérifications sur appareils réels à terminer avant publication - -- [ ] Sur petit téléphone (320 px), parcourir Récap → Voir les détails → Corriger, - retourner au récapitulatif et vérifier date, copie et statistiques. -- [ ] Au clavier, vérifier les pages Détails, Automatismes, Jours non travaillés - et Données et sauvegardes, le focus initial, Tab et Maj+Tab dans chaque dialogue, - Échap et restitution au bouton d’origine. Inclure remplacement et purge. -- [ ] Vérifier Retour du navigateur et Retour de la PWA depuis les pages filles - des réglages et du récapitulatif. -- [ ] Annuler la reprise depuis chacun des deux boutons : aucun changement. - Double appui et deux onglets réels : un seul poste de reprise. -- [ ] Installer la PWA puis démarrer hors ligne, suspendre et rouvrir. - Tester une mise à jour sans interrompre une journée active. -- [ ] Exporter sur iOS/Safari et Android/Chrome, vérifier le fichier téléchargé, - restaurer par fusion puis remplacer sur un profil de test. -- [ ] Simuler IndexedDB inaccessible : erreur visible et Réessayer. -- [ ] Vérifier les libellés d’ajustement de l’import sur un historique endommagé. -- [ ] Vérifier les dates affichées avant purge et récupérer sa sauvegarde. -- [ ] Depuis les réglages puis le rappel, ouvrir Données et sauvegardes, modifier - la durée, recharger et vérifier sa persistance sans suppression de journées. - -### Consigner la recette manuelle - -Utiliser des profils et des données de test. Pour Android/Chrome et iOS/Safari, -prendre une version stable actuelle et consigner les versions exactes. Aucun de -ces contrôles n’a été exécuté sur appareil réel dans cet environnement. - -| Environnement | Appareil et versions OS/navigateur | Date | Résultat | -| --- | --- | --- | --- | -| Android / Chrome, PWA installée | À renseigner | — | À effectuer | -| iOS / Safari, ajout à l’écran d’accueil | À renseigner | — | À effectuer | -| Chromium sur ordinateur, navigation au clavier | À renseigner | — | Recette manuelle à effectuer | - -Pour chaque environnement, relever les résultats d’export/vérification/import -(fusion et remplacement), démarrage hors ligne, suspension/réouverture, Retour -et affichage étroit. Pour les dialogues de remplacement et de purge, relever -aussi le focus initial, Tab/Maj+Tab, Échap et la restitution du focus. Une mise -à jour doit être proposée sans interrompre une journée active. Ne cocher les -vérifications qu’après leur exécution ; consigner les anomalies avec le parcours -et les versions utilisées. - -## Suivi de fiabilité et limites connues - -- La purge au lancement est désactivée pour protéger les imports anciens. - La purge manuelle conserve une sauvegarde dans IndexedDB, récupérable depuis - **Données et sauvegardes**. Modifier la durée de conservation ne supprime rien. - Cette copie n’est pas une sauvegarde externe et disparaît si les - données du site sont effacées. Une seule sauvegarde avant purge est conservée. -- La date du dernier export **préparé** n’est pas une preuve que le - téléchargement a été conservé : le navigateur ne confirme pas cette étape - automatiquement. Une date distincte est marquée vérifiée après relecture du fichier - téléchargé et comparaison de son empreinte complète. Le rappel se déclenche - après sept jours sans export vérifié. La restauration par import et la - récupération après purge sont testées, mais conserver une copie externe reste - nécessaire. -- Les journées dont tous les postes sont automatiques sont signalées comme - déduites et à vérifier. Leur statut métier reste « terminée » ; une étape formelle - de validation et son intégration aux statistiques restent à définir. Cette - fonctionnalité reste au backlog ; la finalisation actuelle conserve les - règles de calcul existantes. -- Tester les navigateurs et téléphones réellement ciblés avec une matrice de - versions, notamment changement d’heure, minuit et suspension prolongée. -- Le nom IndexedDB `TimeLogDB` et l’identifiant `timelog-export` restent stables - pour préserver les données. Les noms visibles et les icônes utilisent Pardis. -- Avant changement de domaine : exporter sur l’ancien domaine, vérifier le fichier, - ouvrir le nouveau domaine, importer et comparer journées, postes et réglages. - Les données locales et le service worker ne migrent pas entre origines. -- La configuration Caddy conserve les règles de revalidation HTML/service worker - et de cache des fichiers hachés ; vérifier les en-têtes sur le domaine publié. diff --git a/README.fr.md b/README.fr.md new file mode 100644 index 0000000..d2917f4 --- /dev/null +++ b/README.fr.md @@ -0,0 +1,418 @@ +# Pardis + +PWA hors ligne de suivi des heures de chantier. Tant que l’application est ouverte, +elle peut démarrer automatiquement la journée et proposer sa clôture. Chaque tâche +commence là où la précédente s’est terminée. Les journées clôturées ont un +récapitulatif concis, prêt à être recopié dans un registre papier. + +Conçue pour l’extérieur : contraste élevé, grandes zones tactiles et utilisable +avec des gants. Les données sont enregistrées localement dans IndexedDB. Une +synchronisation facultative chiffrée de bout en bout les partage entre appareils +autorisés, sans inscription par e-mail ni mot de passe. + +Version anglaise : [README.md](README.md). + +## Développement + +```sh +npm install +npm run dev # http://localhost:5173 +npm test # tests unitaires et de régression IndexedDB +npm run check # vérification Svelte +npm run build # build de production dans dist/ +npm run validate # tests, vérification Svelte et build complet de la PWA +npx playwright install chromium # installer le navigateur pour les tests UI +npm run test:e2e # profils vierges, données synthétiques et test PWA hors ligne +npm run validate:all # inclure les tests navigateur dans la validation complète +``` + +## Exporter et importer les données + +Dans **Réglages → Gérer les données et sauvegardes**, l’écran **Données et +sauvegardes** regroupe l’export, l’import, la vérification des sauvegardes et la +durée de conservation. Le rappel **Sauvegarder** ouvre aussi cet écran. + +Les données restent disponibles dans le navigateur sur cet appareil. La synchronisation +chiffrée peut être activée séparément pour les partager avec vos autres appareils. +Effacer les données du site ou perdre l’appareil peut faire perdre l’historique : +gardez une copie vérifiée du fichier exporté sur un autre support. + +**Exporter les données** télécharge un fichier +`pardis-AAAA-MM-JJ.json`. Il contient toutes les journées conservées, leurs postes +et les réglages enregistrés. L'export fonctionne hors ligne et ne modifie pas les +données de l'appareil. Les identifiants internes des postes sont recréés à l'import. + +Pour restaurer une sauvegarde, choisissez **Importer un fichier…** puis vérifiez +le nom du fichier, les compteurs et les éventuels ajustements signalés. Deux modes +sont disponibles : + +- **Fusionner** ajoute seulement les dates absentes. Une date déjà présente est + conservée intégralement, même si le fichier contient une autre version de ses + postes. Les réglages locaux sont conservés. Importer deux fois le même fichier + ne crée pas de doublons. +- **Remplacer tout** demande une confirmation, puis remplace toutes les journées, + tous les postes et les réglages, y compris une journée locale en cours. Si le + fichier ne contient pas de réglages, les valeurs par défaut sont rétablies. + +Les journées importées sont enregistrées comme terminées. Pour une journée +exportée en cours, le dernier poste est arrêté à l'heure de l'export : le temps +écoulé est conservé. Les intervalles sans travail déjà présents sont conservés. +Une sauvegarde vide est valide et peut servir à restaurer un appareil vide. + +Le format JSON est identifié par `format: "timelog-export"` et `version: 2`. Le format v1 reste importable. Les anciennes applications refusent le format v2 pour éviter de transformer les pauses en travail. +Les fichiers étrangers, versions non prises en charge, dates en double et réglages +invalides sont refusés. Les postes inutilisables et les corrections de plages +horaires sont signalés avant confirmation. La limite de taille est de 20 Mo. +L'écriture est transactionnelle : un échec annule le remplacement ou la fusion +dans son intégralité. Le récapitulatif, le thème et les suggestions sont ensuite +actualisés sans recharger la page. + +Dans **Durée de conservation**, **Conserver (jours)** détermine les journées +anciennes proposées à la purge. Modifier cette durée ne supprime rien ; la purge +automatique est désactivée. L’historique conservé alimente les suggestions et les +statistiques. La dernière journée terminée et les journées actives sont conservées. + +**Purger maintenant** présente les dates concernées et demande confirmation. +Une sauvegarde locale est conservée avant la suppression ; **Récupérer la +sauvegarde avant purge** permet de la fusionner ou de remplacer les données, +puis de l’exporter après restauration. Cette copie est remplacée à la prochaine +purge et disparaît si les données du site sont effacées : un fichier externe +reste nécessaire. + +Les tests d'import/export couvrent le format, les réglages, la fidélité des durées, +les fichiers invalides, la fusion, le remplacement et l'annulation transactionnelle +sur une IndexedDB simulée. `src/lib/fixtures/backup.json` est le jeu d'essai fourni ; +il n'est pas publié avec l'application. + +## Journées, reprise et historique + +- Une journée oubliée est clôturée au retour dans l'application, à son heure de + fin prévue (prolongations comprises), sans jamais précéder la dernière activité + enregistrée. Cette heure est une estimation : l'application ne mesure pas le + travail lorsqu'elle est fermée. Le dernier poste conserve ainsi sa durée jusqu'à + cette limite, au lieu d'être ramené à zéro. +- Une journée terminée aujourd’hui propose **Reprendre la journée** avec confirmation : prolonger le dernier poste, ou créer une pause jusqu’à maintenant (choix initial). Un dernier repas ou une pause garde sa nature ; le travail reprend dans un nouveau poste. Les journées passées se corrigent par **Voir les détails → Corriger cette journée**. +- La création, la clôture et la reprise sont transactionnelles. Les actions + simultanées sur le même poste ne créent pas de doublons ; une journée clôturée + ne peut pas recevoir un nouveau poste actif. La purge épargne les journées + actives et conserve toujours la dernière journée terminée. +- Les horaires de déjeuner affichent un rappel puis une heure indicative de + reprise. Les transitions restent manuelles sauf si l'option **Pause déjeuner** + est activée dans les automatismes. +- Dans **Récap**, le sélecteur **Journée à consulter** permet d'afficher et de copier + chaque journée terminée encore conservée, y compris les journées importées. + +Les tests couvrent notamment les clôtures oubliées, les reprises, les appels +concurrents, les retours d'horloge, les échecs transactionnels et la conservation. + +## Automatismes + +Dans **Réglages → Automatismes**, chaque comportement a son interrupteur : + +| Option | Comportement | +| --- | --- | +| Ouverture | Démarrer aux horaires et jours prévus. Activée par défaut, comme auparavant. | +| Début de journée | Au bouton « Arrivé sur site », proposer la dernière tâche de travail de la précédente journée enregistrée, hors repas et transport. | +| Pause déjeuner | Ouvrir la pause à l'heure habituelle et reprendre la tâche précédente après la durée réglée. Reprendre le type de repas et le restaurant de la précédente journée enregistrée. | +| Arrivée sur chantier | Lors du repas automatique, transformer un transport initial non renseigné en un transport de 1/5/15/30 minutes, selon l'arrondi, suivi de la tâche précédente. | +| Fin de demi-journée | À la pause ou à la clôture, remplacer une plage automatique non renseignée par la dernière tâche de travail connue. Conserver tout poste manuel. | +| Fin de journée | Clôturer à l'heure prévue sans confirmation. | + +Les cinq nouvelles options sont désactivées par défaut. Elles sont conservées +dans les sauvegardes et rétablies lors d'un import en remplacement. L'option +**Arrivée sur chantier** intervient seulement avec un repas automatique. +Sans cette estimation de transport, **Fin de demi-journée** peut remplacer le +transport initial non renseigné par du travail sur toute la plage. Une tâche de +travail déjà présente suffit : aucun doublon n'est ajouté à la demi-journée. + +« Précédente » désigne la dernière journée terminée enregistrée avant la date +courante, y compris après un week-end. La complétion utilise la dernière tâche +de travail connue ; sans historique de travail, elle utilise **Travaux sur site**. +Les postes produits automatiquement portent la mention **Auto**. Les corriger +les rend manuels et les protège des déductions suivantes. Les saisies anciennes +sans cette information sont considérées comme manuelles. + +Une pause commencée ou corrigée manuellement n'est pas terminée par l'automatisme +de déjeuner. Un choix manuel du type ou du restaurant est conservé. Une pause +supprimée n'est pas recréée. Désactiver l'option arrête les transitions suivantes ; +cela ne supprime pas les postes déjà enregistrés. Une durée de déjeuner nulle +n'ajoute aucune pause automatique. La durée d'une pause automatique déjà commencée +est conservée si le réglage change ensuite. + +L'app vérifie l'horloge toutes les 30 secondes et au retour à l'écran. Une PWA +fermée ou suspendue ne peut pas exécuter du code à heure fixe : à la réouverture, +les étapes manquées sont rattrapées avec leurs horaires prévus, sans écraser une +action manuelle plus tardive. Les journées déjà clôturées restent intactes et les +dates entièrement absentes ne sont pas créées. Pour une journée sans intervention, +activer les options voulues et ouvrir l'app au moins une fois pour cette journée. + +Les prolongations et la reprise d'une journée sont respectées. Un démarrage +manuel laisse au moins une heure avant une clôture automatique. Les transitions +automatiques sont transactionnelles : une erreur annule toute l'opération et +des appels simultanés ne créent pas de doublons. + +## Jours non travaillés + +Dans **Réglages → Jours non travaillés**, ajouter les dates à bloquer à l'avance. +La liste est triée par date ; chaque date peut être modifiée ou supprimée. +Les doublons sont refusés. Ces exceptions priment sur les jours de semaine +habituellement travaillés et empêchent l'ouverture automatique, y compris lors +du rattrapage à la réouverture de l'app ou hors ligne. + +Aucune journée ni aucun poste n'est créé pour une date bloquée. Les journées +déjà commencées ou terminées restent conservées. Dans **Journée**, le bouton +**Démarrer quand même** permet un démarrage manuel exceptionnel. +Retirer une date rétablit les règles habituelles de démarrage automatique. + +La liste est enregistrée avec les réglages et incluse dans l'export des données. +Un import en remplacement rétablit la liste de la sauvegarde ; un import en +fusion conserve la liste locale, comme les autres réglages. Une ancienne +sauvegarde sans cette liste la réinitialise à vide lors d'un remplacement. + +## Corriger les postes et les journées terminées + +Le **Total des heures de présence** exclut les pauses et le repas. Le repas reste affiché +séparément dans le récapitulatif et le texte copié. Les totaux suivent le réglage +d’arrondi existant. + +Dans **Journée**, touchez un poste dans **Postes de la journée** pour modifier +son début, sa fin, sa tâche ou son client. Les boutons **Poste ordinaire**, +**Repas** et **Pause** permettent de choisir la nature du poste. Le type interne +d'un transport déjà enregistré est conservé quand on corrige ses horaires ou +son libellé. +Les sélecteurs d'heure ne proposent pas de date : ils conservent la date du +poste. Les minutes et les boutons d'ajustement suivent l'arrondi choisi dans +les réglages (1, 5, 15 ou 30 minutes). Les tâches, clients et restaurants utilisent +les mêmes suggestions et la même saisie libre que l'écran de la journée. +Les corrections sont enregistrées ensemble avec **Enregistrer**. Annuler ne +modifie rien ; ouvrir puis valider un horaire inchangé conserve sa valeur +enregistrée. Le début du premier +poste et la fin du dernier définissent les bornes de la journée. Modifier une +limite intermédiaire ajuste la limite du poste voisin, sans chevauchement. +La fin d'un poste encore en cours reste pilotée par les boutons de changement +de tâche ou de clôture. + +**Ajouter un poste oublié** permet de choisir une plage existante puis les +horaires du poste à insérer dans cette plage. Le début et la fin sont préremplis +avec les deux bornes connues (jusqu'à l'heure actuelle pour un poste en cours). +Il suffit de changer une seule borne pour corriger le début ou la fin de la plage. +Les portions avant et après sont +conservées avec leur tâche et leur client. Par exemple, un poste 08:00–12:00 +peut devenir 08:00–09:00, une tâche oubliée 09:00–10:00, puis 10:00–12:00. +Le bouton **Repas** permet aussi d’ajouter un déjeuner oublié et de renseigner +panier ou restaurant ; **Pause** ajoute une pause hors repas. Pour ajouter juste +après une frontière existante, choisissez le poste qui commence à cette frontière. + +Dans **Récap**, choisissez la date puis **Voir les détails → Corriger cette +journée** : les mêmes actions sont disponibles sans réouverture, même si une autre journée est en +cours. Le récapitulatif et son texte à copier sont recalculés après chaque +enregistrement. La suppression d'un poste demande confirmation ; le voisin +absorbe sa durée, et le dernier poste d'une journée ne peut pas être supprimé. +Supprimer un repas réaffecte sa durée au poste voisin et peut donc changer le +total des heures de présence. + +Chaque correction est transactionnelle. Les horaires inversés, les insertions +hors de la plage choisie et les formulaires devenus périmés après une autre +modification sont refusés sans écriture partielle. + +## Statistiques + +Ouvrez **Réglages → Consulter les statistiques**. Le bouton **Stats** de la barre +du bas n'apparaît que sur cette page ; les autres écrans gardent leurs trois +boutons habituels. La page fonctionne hors ligne. Les calculs de statistiques restent sur l’appareil ; +la synchronisation facultative peut partager leur historique source chiffré. + +Les filtres couvrent tout l'historique, le mois, l'année, les 30 derniers jours +ou une plage de dates inclusive. Les journées en cours sont exclues par défaut. +L'option pour les inclure affiche des cumuls et détails provisoires. Les moyennes +de « Repères et observations », la médiane, les extrêmes et les profils par jour +de semaine restent calculés sur les seules journées terminées enregistrées. + +Les statistiques présentent : + +- Les heures de présence hors repas et pauses, les pauses, le transport identifié, les nombres de + journées, postes, tâches et clients. +- Les heures de présence par client et par tâche, leurs parts des heures de présence, les nombres de + journées concernées et les détails croisés, accessibles en ouvrant une ligne. +- L'évolution par mois ou semaine, sans inventer des heures pour les jours absents. +- Les journées avec repas panier ou restaurant, les restaurants fréquentés, + les durées de pause et les journées sans pause enregistrée. Plusieurs fragments + de repas le même jour ne créent pas plusieurs journées de repas. +- Les moyennes, la médiane, les journées extrêmes, les principales concentrations + de temps par client et tâche, et le détail quotidien. +- Les clients ou restaurants non renseignés, les postes à zéro après arrondi, + les journées vides, les plages non couvertes et les chevauchements éventuels. + +Le calcul reprend l'arrondi du récapitulatif et exclut toujours le repas et les pauses des heures de présence. +Seules les données encore conservées sont analysées : les jours absents ou purgés +ne permettent pas de conclure à une absence de travail. Le transport identifié +repose sur le type enregistré, pas sur une déduction à partir du nom de la tâche. + +En développement, la console expose `timelog.seed()` pour générer 20 jours +d’historique réaliste (utile pour tester le moteur de suggestions) et +`timelog.wipe()` pour l’effacer. + +## Déploiement + +La pile comprend l’application statique servie par Caddy et une API de synchronisation +facultative adossée à SQLite dans un volume persistant. Caddy obtient et renouvelle +le certificat HTTPS. La synchronisation reste désactivée dans chaque client jusqu’à +son activation explicite. Voir [SYNC.md](SYNC.md) pour l’association, le chiffrement, +la récupération, les quotas et les sauvegardes du serveur. + +```sh +cp .env.example .env +$EDITOR .env # renseigner SITE_ADDRESS, ACME_EMAIL et TRUST_PROXY +docker compose up -d --build +``` + +Prérequis : + +- `SITE_ADDRESS` doit déjà pointer vers le serveur. +- `ACME_EMAIL` est obligatoire et doit être une adresse de contact valide. +- Les ports **80 et 443** doivent être accessibles depuis Internet. Le port 80 est + obligatoire : le défi ACME l’utilise pour vérifier la propriété du domaine. + +Les certificats sont stockés dans le répertoire monté `./caddy/data`. Conservez-le : +sans lui, Caddy redemande un certificat à chaque recréation et atteindra la limite +de requêtes de Let's Encrypt après quelques redémarrages. + +Pour mettre à jour, récupérez les changements et reconstruisez l’image : + +```sh +git pull && docker compose up -d --build +``` + +Les clients déjà ouverts ne se rechargent pas seuls. Chaque application vérifie +toutes les heures si une nouvelle version est disponible et affiche une bannière +**« Installer »**, uniquement lorsqu’aucune journée n’est en cours, afin qu’une +mise à jour n’interrompe jamais une journée de travail. + +### HTTPS est obligatoire + +Les navigateurs n’enregistrent un service worker que dans un contexte sécurisé. En +`http://` simple, sur une adresse IP ou un nom d’hôte, l’application se charge mais +n’a **pas de fonctionnement hors ligne, ne peut pas être installée et ne se met jamais +à jour** - ce qui annule l’intérêt de sa conception. `localhost` est la seule +exception, d’où le fonctionnement de `npm run dev` sans certificat. + +### Derrière un reverse proxy existant + +La configuration Caddy actuelle termine elle-même TLS et accepte le protocole PROXY +depuis l’adresse ou le CIDR défini dans `TRUST_PROXY`. Indiquez l’adresse réelle du +proxy amont ; `192.0.2.10/32` dans `.env.example` est une valeur fictive de documentation. +`LISTEN_IP` sélectionne l’interface locale des ports publiés et vaut `0.0.0.0` par +défaut. Un déploiement qui termine TLS ailleurs doit disposer de sa propre configuration +Caddy et de ports ; modifier `SITE_ADDRESS` seul ne configure pas ce mode. + +L’application doit être servie à la racine de son domaine. `start_url` et la portée +du service worker valent tous deux `/` : un hébergement dans un sous-répertoire ne +fonctionnera pas sans modifier le manifeste et `base` dans Vite. + +## Architecture + +Deux décisions structurent le reste de l’application. + +**Les horodatages bruts sont enregistrés ; l’arrondi n’est appliqué qu’à l’affichage.** +Modifier l’arrondi corrige donc aussi les journées passées. Deux postes adjacents +partagent le même instant enregistré - la fin du poste *N* est le début du poste +*N+1* - afin que l’arrondi ne puisse jamais créer de trou entre eux. Les sélecteurs +d’heure suivent la grille d’arrondi choisie ; ouvrir puis valider un horaire inchangé +préserve son horodatage. Les récapitulatifs appliquent le même arrondi à l’affichage. + +**Les suggestions sont déduites de l’historique, pas de tables de compteurs.** Rien +n’est à synchroniser : le modèle est reconstruit en mémoire à partir des postes. +En contrepartie, une durée de conservation courte dégrade les prédictions ; c’est +pourquoi elle est de deux ans par défaut. + +``` +src/lib/ + time.js dates locales, arrondi, formatage (pur) + predict.js moteur de suggestions (pur) + resume.js génération du texte du registre (pur) + db.js schéma Dexie et invariant de chaîne + day.svelte.js machine à états du cycle de journée +``` + +Les fonctions pures et les opérations de base de données et de cycle de vie ont des +tests de régression. `db.js` regroupe les modifications des journées et des postes +dans des transactions communes, notamment la création, la clôture, la reprise et la +correction des bornes. + +## Pauses et nouveaux parcours + +**Journée → Commencer une pause** ferme le poste courant. **Reprendre le travail** +propose la dernière tâche de travail et son client, immédiatement modifiables. +Une pause a ses propres horaires et son libellé, sans client ni type de repas. +Le détail montre tous les postes ; la consultation reste séparée de la correction. +Le récapitulatif et sa copie excluent les pauses. Le transport entre dans le total +des heures de présence, les repas et pauses en sont exclus. Seules les statistiques montrent le +cumul des pauses, séparément des repas. Les intervalles vides importés sont préservés. + +**Réglages → Gérer les automatismes** et **Gérer les jours non travaillés** ouvrent +des pages dédiées ; le bouton Retour du navigateur fonctionne via les routes hash. +**Gérer les données et sauvegardes** ouvre aussi une page dédiée, avec retour aux +réglages, conservation de la durée après rechargement et accès hors ligne. +Les réglages sont écrits par transaction sur la dernière valeur persistée pour +préserver les modifications d’autres onglets. Les erreurs de démarrage et +actualisation sont affichées avec une action Réessayer. + +La checklist avant production et les validations restant à exécuter sont suivies +dans [PRODUCTION.md](PRODUCTION.md). + +## Sauvegarde vérifiée et validation navigateur + +**Exporter les données** prépare le fichier et enregistre son empreinte complète. +**Vérifier le fichier exporté…** relit le fichier téléchargé, contrôle qu’il est +importable et compare son contenu à l’export. La date de la dernière sauvegarde +vérifiée est distincte de celle du dernier export préparé. Après sept jours sans +export vérifié, Pardis affiche un rappel pendant son utilisation. L’export reste local ; conservez le fichier hors de cet appareil. La synchronisation +facultative utilise un coffre distinct et ne remplace pas cette sauvegarde. + +La purge affiche une liste figée de journées. Sauvegarde et suppression partagent +une transaction ; une correction concurrente bloque la purge et demande une +nouvelle consultation. Les autres journées récemment importées restent intactes. + +Les tests Playwright utilisent des contextes neufs et des données synthétiques, +à 320 px. Ils vérifient consultation/correction, focus, Retour, reprise à deux +onglets, export vérifié, purge/restauration et rechargement hors ligne. Ils couvrent +aussi le nouvel écran Données, le retour du champ de conservation à sa valeur +persistée après un échec, le refus d’un export modifié et l’annulation complète +d’une purge en erreur, avec préservation de la sauvegarde précédente. Traces, +vidéos et captures sont désactivées. Pour un Chrome déjà installé, définir +`PARDIS_CHROME_PATH` avec son chemin ; sinon installer Chromium avec Playwright. +La CI installe Chromium Headless Shell et ses dépendances avant de lancer ces +tests. Elle télécharge uniquement la variante utilisée par les tests sans interface +et accorde 180 secondes à chaque connexion de téléchargement, avec les nouvelles +tentatives intégrées à Playwright. L’installation dispose de 20 minutes et le job +de validation de 30 minutes. Une erreur d’installation bloque le déploiement. + + +## Synchronisation facultative entre appareils + +**Données et sauvegardes → Synchronisation et appareils** permet de créer un coffre +chiffré sans e-mail ni mot de passe, puis de lier des appareils avec un code temporaire +ou un QR code et une confirmation sur l’écran dédié. La saisie reste locale et +fonctionne hors ligne. Les journées concurrentes sont conservées pour résolution. +Un document de secours permet de retrouver l’accès après perte des appareils. + +Les règles métier sont partagées ; le thème et le suivi des sauvegardes restent +locaux. Un appareil est responsable des automatismes, avec proposition de transfert +à l’ouverture sur un appareil secondaire. Les demandes d’association ne produisent +aucune alerte ailleurs dans l’application. + +Le protocole, les limites de sécurité, le fonctionnement des suppressions et les +commandes de sauvegarde/restauration du serveur sont documentés dans [SYNC.md](SYNC.md). +Pour le développement, lancer aussi `npm run sync:server` avec Node.js 24. + +## Licence et crédits typographiques + +Le code source de Pardis est distribué sous la licence GNU General Public License, +version 3 uniquement (GPL-3.0-only). Le texte intégral se trouve dans +[LICENSE.md](LICENSE.md). Les fichiers de polices fournis sont des œuvres distinctes, +distribuées sous la SIL Open Font License, version 1.1. Leurs mentions de droits +d’auteur et le texte de cette licence figurent dans [public/fonts/OFL.txt](public/fonts/OFL.txt). +Copyright (C) 2026 EI Adrien Weiskopff. + +- **Bebas Neue**, par Ryoichi Tsunekawa - SIL Open Font License 1.1. +- **Charis SIL** - SIL Open Font License 1.1. Voir le [site officiel de Charis SIL](https://software.sil.org/fr/charis/). diff --git a/README.md b/README.md index 1e338ae..af830e7 100644 --- a/README.md +++ b/README.md @@ -1,395 +1,394 @@ # Pardis -Offline-first PWA for tracking hours on site. While the app is running, it can start -the day automatically and prompt for its end. Each task begins where the previous -one ended. Closed days have concise summaries ready to copy into a paper logbook. +An offline-first progressive web app (PWA) for tracking on-site working hours. While +the app is open, it can start a workday automatically and prompt to close it. Each +task starts when the previous one ends. Closed days have concise summaries ready to +copy into a paper logbook. -Built for use outdoors: high contrast, large targets, usable with gloves on. Data -is saved locally in IndexedDB. Optional end-to-end encrypted synchronization shares -it between authorized devices without e-mail or password registration. +Designed for outdoor use: high contrast, large touch targets, and usable with gloves. +Data is stored locally in IndexedDB. Optional end-to-end encrypted synchronization +shares it between authorized devices without requiring an email address or password. + +French documentation: [README.fr.md](README.fr.md). ## Development -```sh -npm install -npm run dev # http://localhost:5173 -npm test # unit and IndexedDB regression tests -npm run check # svelte-check -npm run build # production build into dist/ -npm run validate # tests, Svelte check and full PWA build -npx playwright install chromium # install the browser for UI tests -npm run test:e2e # fresh profiles, synthetic fixtures and offline PWA test -npm run validate:all # include browser tests in full validation -``` + npm install + npm run dev # http://localhost:5173 + npm test # unit and IndexedDB regression tests + npm run check # Svelte diagnostics + npm run build # production build into dist/ + npm run validate # tests, Svelte diagnostics and full PWA build + npx playwright install chromium # install the browser for UI tests + npm run test:e2e # fresh profiles, synthetic fixtures and offline PWA test + npm run validate:all # include browser tests in full validation -## Exporter et importer les données +## Exporting and importing data -Dans **Réglages → Gérer les données et sauvegardes**, l’écran **Données et -sauvegardes** regroupe l’export, l’import, la vérification des sauvegardes et la -durée de conservation. Le rappel **Sauvegarder** ouvre aussi cet écran. +In **Settings (Réglages) → Manage data and backups (Gérer les données et +sauvegardes)**, the **Data and backups (Données et sauvegardes)** screen groups +export, import, backup verification, and retention settings. The **Back up +(Sauvegarder)** reminder also opens this screen. -Les données restent disponibles dans le navigateur sur cet appareil. La synchronisation -chiffrée peut être activée séparément pour les partager avec vos autres appareils. -Effacer les données du site ou perdre l’appareil peut faire perdre l’historique : -gardez une copie vérifiée du fichier exporté sur un autre support. +Data remains available in the browser on this device. Optional encrypted +synchronization can share it with your other devices. Clearing site data or losing +the device can result in lost history: keep a verified copy of the exported file on +another storage device. -**Exporter les données** télécharge un fichier -`pardis-AAAA-MM-JJ.json`. Il contient toutes les journées conservées, leurs postes -et les réglages enregistrés. L'export fonctionne hors ligne et ne modifie pas les -données de l'appareil. Les identifiants internes des postes sont recréés à l'import. +**Export data (Exporter les données)** downloads a file named +pardis-YYYY-MM-DD.json. It contains all retained days, their entries, and saved +settings. Export works offline and does not modify the device's data. Internal entry +identifiers are regenerated on import. -Pour restaurer une sauvegarde, choisissez **Importer un fichier…** puis vérifiez -le nom du fichier, les compteurs et les éventuels ajustements signalés. Deux modes -sont disponibles : +To restore a backup, choose **Import file… (Importer un fichier…)**, then review the +file name, counts, and any reported adjustments. Two modes are available: -- **Fusionner** ajoute seulement les dates absentes. Une date déjà présente est - conservée intégralement, même si le fichier contient une autre version de ses - postes. Les réglages locaux sont conservés. Importer deux fois le même fichier - ne crée pas de doublons. -- **Remplacer tout** demande une confirmation, puis remplace toutes les journées, - tous les postes et les réglages, y compris une journée locale en cours. Si le - fichier ne contient pas de réglages, les valeurs par défaut sont rétablies. +- **Merge (Fusionner)** adds only dates that are not already present. An existing + date is kept in full even if the file contains a different version of its entries. + Local settings are preserved. Importing the same file twice creates no duplicates. +- **Replace all (Remplacer tout)** asks for confirmation, then replaces every day, + entry, and setting, including a local day currently in progress. If the file has no + settings, defaults are restored. -Les journées importées sont enregistrées comme terminées. Pour une journée -exportée en cours, le dernier poste est arrêté à l'heure de l'export : le temps -écoulé est conservé. Les intervalles sans travail déjà présents sont conservés. -Une sauvegarde vide est valide et peut servir à restaurer un appareil vide. +Imported days are marked as closed. For a day exported while in progress, the last +entry is stopped at the export time, preserving elapsed time. Existing gaps between +work periods are preserved. An empty backup is valid and can restore an empty device. -Le format JSON est identifié par `format: "timelog-export"` et `version: 2`. Le format v1 reste importable. Les anciennes applications refusent le format v2 pour éviter de transformer les pauses en travail. -Les fichiers étrangers, versions non prises en charge, dates en double et réglages -invalides sont refusés. Les postes inutilisables et les corrections de plages -horaires sont signalés avant confirmation. La limite de taille est de 20 Mo. -L'écriture est transactionnelle : un échec annule le remplacement ou la fusion -dans son intégralité. Le récapitulatif, le thème et les suggestions sont ensuite -actualisés sans recharger la page. +The JSON format is identified by format: "timelog-export" and version: 2. Version 1 +files remain importable. Older app versions reject version 2 to avoid turning breaks +into work. Foreign files, unsupported versions, duplicate dates, and invalid +settings are rejected. Unusable entries and time-range corrections are reported +before confirmation. The size limit is 20 MB. Writes are transactional: a failure +cancels the entire merge or replacement. The summary, theme, and suggestions then +refresh without reloading the page. -Dans **Durée de conservation**, **Conserver (jours)** détermine les journées -anciennes proposées à la purge. Modifier cette durée ne supprime rien ; la purge -automatique est désactivée. L’historique conservé alimente les suggestions et les -statistiques. La dernière journée terminée et les journées actives sont conservées. +Under **Retention (Durée de conservation)**, **Keep (days) (Conserver (jours))** +determines which older days are offered for deletion. Changing this setting does not +delete anything; automatic deletion is disabled. Retained history feeds suggestions +and statistics. The latest closed day and active days are always kept. -**Purger maintenant** présente les dates concernées et demande confirmation. -Une sauvegarde locale est conservée avant la suppression ; **Récupérer la -sauvegarde avant purge** permet de la fusionner ou de remplacer les données, -puis de l’exporter après restauration. Cette copie est remplacée à la prochaine -purge et disparaît si les données du site sont effacées : un fichier externe -reste nécessaire. +**Delete now (Purger maintenant)** lists affected dates and asks for confirmation. +A local backup is kept before deletion. **Recover pre-deletion backup (Récupérer la +sauvegarde avant purge)** can merge or replace data, then export it after recovery. +This copy is replaced by the next deletion and is removed if site data is cleared; an +external file is still necessary. -Les tests d'import/export couvrent le format, les réglages, la fidélité des durées, -les fichiers invalides, la fusion, le remplacement et l'annulation transactionnelle -sur une IndexedDB simulée. `src/lib/fixtures/backup.json` est le jeu d'essai fourni ; -il n'est pas publié avec l'application. +Import/export tests cover the format, settings, duration fidelity, invalid files, +merge, replacement, and transaction rollback using a simulated IndexedDB. +src/lib/fixtures/backup.json is the supplied test fixture; it is not published with +the app. -## Journées, reprise et historique +## Workdays, resuming, and history -- Une journée oubliée est clôturée au retour dans l'application, à son heure de - fin prévue (prolongations comprises), sans jamais précéder la dernière activité - enregistrée. Cette heure est une estimation : l'application ne mesure pas le - travail lorsqu'elle est fermée. Le dernier poste conserve ainsi sa durée jusqu'à - cette limite, au lieu d'être ramené à zéro. -- Une journée terminée aujourd’hui propose **Reprendre la journée** avec confirmation : prolonger le dernier poste, ou créer une pause jusqu’à maintenant (choix initial). Un dernier repas ou une pause garde sa nature ; le travail reprend dans un nouveau poste. Les journées passées se corrigent par **Voir les détails → Corriger cette journée**. -- La création, la clôture et la reprise sont transactionnelles. Les actions - simultanées sur le même poste ne créent pas de doublons ; une journée clôturée - ne peut pas recevoir un nouveau poste actif. La purge épargne les journées - actives et conserve toujours la dernière journée terminée. -- Les horaires de déjeuner affichent un rappel puis une heure indicative de - reprise. Les transitions restent manuelles sauf si l'option **Pause déjeuner** - est activée dans les automatismes. -- Dans **Récap**, le sélecteur **Journée à consulter** permet d'afficher et de copier - chaque journée terminée encore conservée, y compris les journées importées. +- If a workday was left open, it is closed when the app is next opened, at its planned + end time (including overtime), but never before the last recorded activity. This is + an estimate: the app does not measure work while it is closed. The final entry + keeps its duration up to that limit instead of being reset to zero. +- A day closed today offers **Resume workday (Reprendre la journée)** with + confirmation: continue the last entry, or create a break up to now (the default). + A final meal or break keeps its type; work resumes in a new entry. Past days can be + corrected via **View details → Edit this day (Voir les détails → Corriger cette + journée)**. +- Creating, closing, and resuming a day are transactional. Simultaneous actions on + the same entry cannot create duplicates; a closed day cannot receive a new active + entry. Deletion skips active days and always keeps the latest closed day. +- Lunch schedules show a reminder and an indicative return time. Transitions remain + manual unless **Lunch break (Pause déjeuner)** is enabled under automations. +- In **Summary (Récap)**, the **Day to view (Journée à consulter)** selector shows + and copies every retained closed day, including imported days. -Les tests couvrent notamment les clôtures oubliées, les reprises, les appels -concurrents, les retours d'horloge, les échecs transactionnels et la conservation. +Tests cover missed closures, resuming, concurrent calls, clock changes, transaction +failures, and data retention. -## Automatismes +## Automations -Dans **Réglages → Automatismes**, chaque comportement a son interrupteur : +In **Settings (Réglages) → Automations (Automatismes)**, each behavior has its own +switch: -| Option | Comportement | +| Option | Behavior | | --- | --- | -| Ouverture | Démarrer aux horaires et jours prévus. Activée par défaut, comme auparavant. | -| Début de journée | Au bouton « Arrivé sur site », proposer la dernière tâche de travail de la précédente journée enregistrée, hors repas et transport. | -| Pause déjeuner | Ouvrir la pause à l'heure habituelle et reprendre la tâche précédente après la durée réglée. Reprendre le type de repas et le restaurant de la précédente journée enregistrée. | -| Arrivée sur chantier | Lors du repas automatique, transformer un transport initial non renseigné en un transport de 1/5/15/30 minutes, selon l'arrondi, suivi de la tâche précédente. | -| Fin de demi-journée | À la pause ou à la clôture, remplacer une plage automatique non renseignée par la dernière tâche de travail connue. Conserver tout poste manuel. | -| Fin de journée | Clôturer à l'heure prévue sans confirmation. | +| Opening | Start at the scheduled times and on scheduled days. Enabled by default, as before. | +| Start of day | When tapping “Arrived on site” (Arrivé sur site), suggest the last work task from the previous recorded day, excluding meals and travel. | +| Lunch break | Start a break at the usual time and resume the previous task after the configured duration. Reuse the meal type and restaurant from the previous recorded day. | +| Site arrival | During an automatic meal, turn an unspecified initial travel period into a travel entry of 1/5/15/30 minutes, according to the rounding setting, followed by the previous task. | +| Half-day end | At a break or day closure, replace an unspecified automatic period with the last known work task. Preserve any manual entry. | +| End of day | Close the day at the scheduled time without confirmation. | -Les cinq nouvelles options sont désactivées par défaut. Elles sont conservées -dans les sauvegardes et rétablies lors d'un import en remplacement. L'option -**Arrivée sur chantier** intervient seulement avec un repas automatique. -Sans cette estimation de transport, **Fin de demi-journée** peut remplacer le -transport initial non renseigné par du travail sur toute la plage. Une tâche de -travail déjà présente suffit : aucun doublon n'est ajouté à la demi-journée. +The five newer options are disabled by default. They are included in backups and are +restored by replacement imports. **Site arrival** only applies with an automatic +meal. Without this travel estimate, **Half-day end** may replace unspecified initial +travel with work for the entire period. If a work task is already present, no +duplicate is added to the half-day. -« Précédente » désigne la dernière journée terminée enregistrée avant la date -courante, y compris après un week-end. La complétion utilise la dernière tâche -de travail connue ; sans historique de travail, elle utilise **Travaux sur site**. -Les postes produits automatiquement portent la mention **Auto**. Les corriger -les rend manuels et les protège des déductions suivantes. Les saisies anciennes -sans cette information sont considérées comme manuelles. +“Previous” means the latest recorded closed day before the current date, including +after a weekend. Completion reuses the most recent known work task; when there is no +work history, it uses “On-site work” (Travaux sur site). Automatically created +entries are marked **Auto**. Editing one makes it manual and protects it from later +inference. Older entries without this marker are treated as manual. -Une pause commencée ou corrigée manuellement n'est pas terminée par l'automatisme -de déjeuner. Un choix manuel du type ou du restaurant est conservé. Une pause -supprimée n'est pas recréée. Désactiver l'option arrête les transitions suivantes ; -cela ne supprime pas les postes déjà enregistrés. Une durée de déjeuner nulle -n'ajoute aucune pause automatique. La durée d'une pause automatique déjà commencée -est conservée si le réglage change ensuite. +A manually started or edited break is not ended by the lunch automation. A manually +chosen meal type or restaurant is preserved. A deleted break is not recreated. +Disabling an option stops future transitions; it does not remove entries already +saved. A zero lunch duration does not add an automatic break. The duration of an +automatic break already in progress is preserved if the setting changes later. -L'app vérifie l'horloge toutes les 30 secondes et au retour à l'écran. Une PWA -fermée ou suspendue ne peut pas exécuter du code à heure fixe : à la réouverture, -les étapes manquées sont rattrapées avec leurs horaires prévus, sans écraser une -action manuelle plus tardive. Les journées déjà clôturées restent intactes et les -dates entièrement absentes ne sont pas créées. Pour une journée sans intervention, -activer les options voulues et ouvrir l'app au moins une fois pour cette journée. +The app checks the clock every 30 seconds and when it returns to the foreground. +A closed or suspended PWA cannot run code at a scheduled time: on reopening, missed +steps are caught up at their planned times without overwriting a later manual action. +Already closed days remain unchanged, and entirely missing dates are not created. +For a day with no intervention, enable the desired options and open the app at least +once that day. -Les prolongations et la reprise d'une journée sont respectées. Un démarrage -manuel laisse au moins une heure avant une clôture automatique. Les transitions -automatiques sont transactionnelles : une erreur annule toute l'opération et -des appels simultanés ne créent pas de doublons. +Overtime and resumed days are respected. A manual start leaves at least one hour +before an automatic closure. Automatic transitions are transactional: an error +cancels the whole operation, and simultaneous calls cannot create duplicates. -## Jours non travaillés +## Non-working days -Dans **Réglages → Jours non travaillés**, ajouter les dates à bloquer à l'avance. -La liste est triée par date ; chaque date peut être modifiée ou supprimée. -Les doublons sont refusés. Ces exceptions priment sur les jours de semaine -habituellement travaillés et empêchent l'ouverture automatique, y compris lors -du rattrapage à la réouverture de l'app ou hors ligne. +In **Settings (Réglages) → Non-working days (Jours non travaillés)**, add dates to +block in advance. The list is sorted by date; each date can be edited or removed. +Duplicates are rejected. These exceptions take precedence over the usual weekdays +and prevent automatic opening, including catch-up when the app is reopened or used +offline. -Aucune journée ni aucun poste n'est créé pour une date bloquée. Les journées -déjà commencées ou terminées restent conservées. Dans **Journée**, le bouton -**Démarrer quand même** permet un démarrage manuel exceptionnel. -Retirer une date rétablit les règles habituelles de démarrage automatique. +No day or entry is created for a blocked date. Days already started or closed are +preserved. **Start anyway (Démarrer quand même)** in **Workday (Journée)** allows an +exceptional manual start. Removing a date restores the usual automatic start rules. -La liste est enregistrée avec les réglages et incluse dans l'export des données. -Un import en remplacement rétablit la liste de la sauvegarde ; un import en -fusion conserve la liste locale, comme les autres réglages. Une ancienne -sauvegarde sans cette liste la réinitialise à vide lors d'un remplacement. +The list is saved with settings and included in data exports. A replacement import +restores the backup's list; a merge keeps the local list, like other settings. An +older backup without this list resets it to empty on replacement. -## Corriger les postes et les journées terminées +## Editing entries and closed days -Le **Total des heures de présence** exclut les pauses et le repas. Le repas reste affiché -séparément dans le récapitulatif et le texte copié. Les totaux suivent le réglage -d’arrondi existant. +The **Total hours present (Total des heures de présence)** excludes breaks and meals. +Meals remain separate in the summary and copied text. Totals use the selected +rounding setting. -Dans **Journée**, touchez un poste dans **Postes de la journée** pour modifier -son début, sa fin, sa tâche ou son client. Les boutons **Poste ordinaire**, -**Repas** et **Pause** permettent de choisir la nature du poste. Le type interne -d'un transport déjà enregistré est conservé quand on corrige ses horaires ou -son libellé. -Les sélecteurs d'heure ne proposent pas de date : ils conservent la date du -poste. Les minutes et les boutons d'ajustement suivent l'arrondi choisi dans -les réglages (1, 5, 15 ou 30 minutes). Les tâches, clients et restaurants utilisent -les mêmes suggestions et la même saisie libre que l'écran de la journée. -Les corrections sont enregistrées ensemble avec **Enregistrer**. Annuler ne -modifie rien ; ouvrir puis valider un horaire inchangé conserve sa valeur -enregistrée. Le début du premier -poste et la fin du dernier définissent les bornes de la journée. Modifier une -limite intermédiaire ajuste la limite du poste voisin, sans chevauchement. -La fin d'un poste encore en cours reste pilotée par les boutons de changement -de tâche ou de clôture. +In **Workday (Journée)**, tap an entry under **Day entries (Postes de la journée)** +to edit its start, end, task, or client. **Regular entry (Poste ordinaire)**, **Meal +(Repas)**, and **Break (Pause)** buttons select the entry type. When editing the +times or label of an existing travel entry, its internal type is preserved. -**Ajouter un poste oublié** permet de choisir une plage existante puis les -horaires du poste à insérer dans cette plage. Le début et la fin sont préremplis -avec les deux bornes connues (jusqu'à l'heure actuelle pour un poste en cours). -Il suffit de changer une seule borne pour corriger le début ou la fin de la plage. -Les portions avant et après sont -conservées avec leur tâche et leur client. Par exemple, un poste 08:00–12:00 -peut devenir 08:00–09:00, une tâche oubliée 09:00–10:00, puis 10:00–12:00. -Le bouton **Repas** permet aussi d’ajouter un déjeuner oublié et de renseigner -panier ou restaurant ; **Pause** ajoute une pause hors repas. Pour ajouter juste -après une frontière existante, choisissez le poste qui commence à cette frontière. +Time selectors do not include a date: they keep the entry's date. Minutes and +adjustment buttons follow the selected rounding interval (1, 5, 15, or 30 minutes). +Tasks, clients, and restaurants use the same suggestions and free-text entry as the +workday screen. -Dans **Récap**, choisissez la date puis **Voir les détails → Corriger cette -journée** : les mêmes actions sont disponibles sans réouverture, même si une autre journée est en -cours. Le récapitulatif et son texte à copier sont recalculés après chaque -enregistrement. La suppression d'un poste demande confirmation ; le voisin -absorbe sa durée, et le dernier poste d'une journée ne peut pas être supprimé. -Supprimer un repas réaffecte sa durée au poste voisin et peut donc changer le -total des heures de présence. +Corrections are saved together with **Save (Enregistrer)**. Cancel makes no changes; +opening and saving an unchanged time preserves its stored value. The first entry's +start and last entry's end define the day boundaries. Editing an intermediate +boundary adjusts the adjacent entry's boundary without overlap. The end of an active +entry remains controlled by task changes or day closure. -Chaque correction est transactionnelle. Les horaires inversés, les insertions -hors de la plage choisie et les formulaires devenus périmés après une autre -modification sont refusés sans écriture partielle. +**Add a missed entry (Ajouter un poste oublié)** lets you choose an existing period, +then the times for an entry to insert into it. The start and end are prefilled with +the known boundaries (up to now for an active entry). Change either boundary to +adjust one side of the period. The portions before and after are preserved with +their original task and client. For example, 08:00–12:00 can become 08:00–09:00, +a missed task from 09:00–10:00, then 10:00–12:00. **Meal (Repas)** can add a missed +meal and set its type to packed or restaurant; **Break (Pause)** adds a non-meal +break. To add an entry immediately after an existing boundary, choose the entry +that starts at that boundary. -## Statistiques +In **Summary (Récap)**, choose a date, then **View details → Edit this day (Voir les +détails → Corriger cette journée)**. The same actions are available without resuming +the day, even while another day is in progress. The summary and copied text are +recalculated after each save. Deleting an entry requires confirmation; its neighbor +absorbs its duration, and the final entry of a day cannot be deleted. Deleting a meal +assigns its duration to the neighboring entry and can therefore change total hours +present. -Ouvrez **Réglages → Consulter les statistiques**. Le bouton **Stats** de la barre -du bas n'apparaît que sur cette page ; les autres écrans gardent leurs trois -boutons habituels. La page fonctionne hors ligne. Les calculs de statistiques restent sur l’appareil ; -la synchronisation facultative peut partager leur historique source chiffré. +Every correction is transactional. Reversed times, insertions outside the selected +period, and forms made stale by another edit are rejected without a partial write. -Les filtres couvrent tout l'historique, le mois, l'année, les 30 derniers jours -ou une plage de dates inclusive. Les journées en cours sont exclues par défaut. -L'option pour les inclure affiche des cumuls et détails provisoires. Les moyennes -de « Repères et observations », la médiane, les extrêmes et les profils par jour -de semaine restent calculés sur les seules journées terminées enregistrées. +## Statistics -Les statistiques présentent : +Open **Settings (Réglages) → View statistics (Consulter les statistiques)**. The +**Stats** button appears in the bottom bar only on this page; other screens keep +their usual three buttons. The page works offline. Statistics are calculated on the +device; optional synchronization may share their source history in encrypted form. -- Les heures de présence hors repas et pauses, les pauses, le transport identifié, les nombres de - journées, postes, tâches et clients. -- Les heures de présence par client et par tâche, leurs parts des heures de présence, les nombres de - journées concernées et les détails croisés, accessibles en ouvrant une ligne. -- L'évolution par mois ou semaine, sans inventer des heures pour les jours absents. -- Les journées avec repas panier ou restaurant, les restaurants fréquentés, - les durées de pause et les journées sans pause enregistrée. Plusieurs fragments - de repas le même jour ne créent pas plusieurs journées de repas. -- Les moyennes, la médiane, les journées extrêmes, les principales concentrations - de temps par client et tâche, et le détail quotidien. -- Les clients ou restaurants non renseignés, les postes à zéro après arrondi, - les journées vides, les plages non couvertes et les chevauchements éventuels. +Filters cover all history, the month, the year, the last 30 days, or an inclusive +date range. Days in progress are excluded by default. Enabling them shows +provisional totals and details. Averages under **Highlights and observations +(Repères et observations)**, the median, extremes, and weekday profiles use only +recorded closed days. -Le calcul reprend l'arrondi du récapitulatif et exclut toujours le repas et les pauses des heures de présence. -Seules les données encore conservées sont analysées : les jours absents ou purgés -ne permettent pas de conclure à une absence de travail. Le transport identifié -repose sur le type enregistré, pas sur une déduction à partir du nom de la tâche. +Statistics include: -In dev the console exposes `timelog.seed()` to generate 20 days of realistic history -(useful for exercising the suggestion engine) and `timelog.wipe()` to clear it. +- Hours present excluding meals and breaks, breaks, identified travel, and counts of + days, entries, tasks, and clients. +- Hours present by client and task, their shares of hours present, the number of + affected days, and cross-details available by opening a row. +- Monthly or weekly trends without inventing hours for missing days. +- Days with packed or restaurant meals, restaurants visited, break durations, and + days with no recorded break. Multiple meal fragments on one day count as one + meal day. +- Averages, median, extreme days, top client and task time concentrations, and + daily details. +- Unspecified clients or restaurants, entries rounded to zero, empty days, uncovered + periods, and possible overlaps. + +Calculations use the summary's rounding and always exclude meals and breaks from +hours present. Only retained data is analyzed: missing or deleted days cannot be +taken as proof that no work happened. Identified travel uses the recorded entry type, +not an inference from the task name. + +In development, the console exposes `timelog.seed()` to generate 20 days of realistic +history (useful for exercising suggestions) and `timelog.wipe()` to clear it. ## Deployment -The stack contains the static app served by Caddy and an optional-use synchronization +The stack consists of the static app served by Caddy and an optional synchronization API backed by SQLite in a persistent volume. Caddy obtains and renews the HTTPS -certificate. Synchronization stays disabled in each client until explicitly activated. -See [SYNC.md](SYNC.md) for pairing, encryption, recovery, quotas and server backups. +certificate. Synchronization remains disabled in each client until explicitly +activated. See [SYNC.md](SYNC.md) for pairing, encryption, recovery, quotas, and +server backups. -```sh -cp .env.example .env -$EDITOR .env # set SITE_ADDRESS, ACME_EMAIL and TRUST_PROXY -docker compose up -d --build -``` + cp .env.example .env + $EDITOR .env # set SITE_ADDRESS, ACME_EMAIL and TRUST_PROXY + docker compose up -d --build Requirements: - `SITE_ADDRESS` must already resolve to the server. - `ACME_EMAIL` is required and must be a valid contact address. -- Ports **80 and 443** must be reachable from the internet. Port 80 is not optional — - it is what the ACME challenge uses to prove domain ownership. +- Ports **80 and 443** must be reachable from the internet. Port 80 is mandatory: + the ACME challenge uses it to prove domain ownership. -Certificates are stored in the bind-mounted `./caddy/data` directory. Keep it: without it Caddy re-issues -on every recreate and will hit the Let's Encrypt rate limit after a handful of restarts. +Certificates are stored in the bind-mounted `./caddy/data` directory. Keep it: +without it, Caddy requests a new certificate after every recreation and will hit +Let's Encrypt rate limits after only a handful of restarts. To update, pull and rebuild: -```sh -git pull && docker compose up -d --build -``` + git pull && docker compose up -d --build -Running clients do not reload on their own. Each app checks hourly for a new build and -offers an **"Installer"** banner — but only while no working day is in progress, so an -update can never interrupt someone mid-day. +Running clients do not reload automatically. Each app checks hourly for a new build +and offers an **Install (Installer)** banner, but only when no workday is in progress, +so an update cannot interrupt a workday. -### HTTPS is not optional +### HTTPS is required -Browsers only register a service worker in a secure context. Served over plain `http://` -to an IP or hostname, the app still loads but has **no offline support, cannot be -installed, and never updates** — which removes the point of the design. `localhost` is -the only exception, which is why `npm run dev` works without certificates. +Browsers only register a service worker in a secure context. Served over plain +`http://` at an IP address or hostname, the app still loads but has **no offline +support, cannot be installed, and never updates**, defeating the design. `localhost` +is the only exception, which is why `npm run dev` works without certificates. ### Behind an existing reverse proxy -The current Caddy configuration terminates TLS itself and accepts PROXY protocol -from the address or CIDR in `TRUST_PROXY`. Set this value to the real upstream -proxy address; `192.0.2.10/32` in `.env.example` is a documentation placeholder. -`LISTEN_IP` selects the local interface for the published ports and defaults to -`0.0.0.0`. A deployment that terminates TLS elsewhere needs its own Caddy and -port configuration; changing `SITE_ADDRESS` alone does not configure that mode. +The current Caddy configuration terminates TLS itself and accepts the PROXY protocol +from the address or CIDR in `TRUST_PROXY`. Set this to the actual upstream proxy +address; `192.0.2.10/32` in `.env.example` is a documentation placeholder. +`LISTEN_IP` selects the local interface for published ports and defaults to +`0.0.0.0`. A deployment that terminates TLS elsewhere needs its own Caddy and port +configuration; changing `SITE_ADDRESS` alone does not configure that mode. -The app must be served from the root of its domain. `start_url` and the service worker -scope are both `/`, so hosting it under a subpath will not work without changing the -manifest and Vite's `base`. +The app must be served from the root of its domain. `start_url` and the service +worker scope are both `/`, so hosting it under a subpath will not work without +changing the manifest and Vite's `base`. ## Architecture Two decisions shape everything else. -**Raw timestamps are stored; rounding is applied only at render.** Changing the rounding -setting therefore corrects past days too. Adjacent entries share one stored instant — -entry *N*'s end *is* entry *N+1*'s start — so rounding can never open a gap between -them. Time pickers follow the selected rounding grid; opening an unchanged time -preserves its stored timestamp. Summaries apply the same rounding at render. +**Raw timestamps are stored; rounding is applied only when rendering.** Changing +the rounding setting therefore corrects past days too. Adjacent entries share one +stored instant - entry *N*'s end is entry *N+1*'s start - so rounding can never open +a gap between them. Time pickers follow the selected rounding grid; opening an +unchanged time preserves its stored timestamp. Summaries apply the same rounding +when rendered. -**Suggestions are derived from history, not from counter tables.** There is nothing to -keep in sync; the model is rebuilt in memory from the entries. The tradeoff is that a -short retention window degrades predictions, which is why retention defaults to two -years. +**Suggestions are derived from history, not counter tables.** Nothing needs to be +kept in sync; the model is rebuilt in memory from entries. The tradeoff is that a +short retention window degrades predictions, which is why retention defaults to +two years. -``` -src/lib/ - time.js local dates, rounding, formatting (pure) - predict.js the suggestion engine (pure) - resume.js logbook text builder (pure) - db.js Dexie schema and the chain invariant - day.svelte.js day lifecycle state machine -``` + src/lib/ + time.js local dates, rounding, formatting (pure) + predict.js suggestion engine (pure) + resume.js logbook text builder (pure) + db.js Dexie schema and chain invariant + day.svelte.js day lifecycle state machine -Pure helpers and database/lifecycle operations have regression tests. `db.js` -keeps day and entry changes in shared transactions, including creation, closure, -reopening and boundary corrections. +Pure helpers and database/lifecycle operations have regression tests. `db.js` keeps +day and entry changes in shared transactions, including creation, closure, resuming, +and boundary corrections. -## Pauses et nouveaux parcours +## Breaks and newer workflows -**Journée → Commencer une pause** ferme le poste courant. **Reprendre le travail** -propose la dernière tâche de travail et son client, immédiatement modifiables. -Une pause a ses propres horaires et son libellé, sans client ni type de repas. -Le détail montre tous les postes ; la consultation reste séparée de la correction. -Le récapitulatif et sa copie excluent les pauses. Le transport entre dans le total -des heures de présence, les repas et pauses en sont exclus. Seules les statistiques montrent le -cumul des pauses, séparément des repas. Les intervalles vides importés sont préservés. +**Workday → Start a break (Journée → Commencer une pause)** closes the current entry. +**Resume work (Reprendre le travail)** suggests the last work task and its client, +which can be edited immediately. A break has its own times and label, without a +client or meal type. Details show every entry; viewing is separate from editing. +Summaries and copied text exclude breaks. Travel is included in total hours present; +meals and breaks are excluded. Statistics show total break time separately from meals. +Imported gaps are preserved. -**Réglages → Gérer les automatismes** et **Gérer les jours non travaillés** ouvrent -des pages dédiées ; le bouton Retour du navigateur fonctionne via les routes hash. -**Gérer les données et sauvegardes** ouvre aussi une page dédiée, avec retour aux -réglages, conservation de la durée après rechargement et accès hors ligne. -Les réglages sont écrits par transaction sur la dernière valeur persistée pour -préserver les modifications d’autres onglets. Les erreurs de démarrage et -actualisation sont affichées avec une action Réessayer. +**Settings (Réglages) → Manage automations (Gérer les automatismes)** and +**Manage non-working days (Gérer les jours non travaillés)** open dedicated pages; +the browser Back button works through hash routes. **Manage data and backups (Gérer +les données et sauvegardes)** also opens a dedicated page, with a return to settings, +retention persistence after reload, and offline access. -La checklist avant production et les validations restant à exécuter sont suivies -dans [PRODUCTION.md](PRODUCTION.md). +Settings are written transactionally against the latest persisted value to preserve +changes made in other tabs. Startup and refresh errors are shown with a Retry action. -## Sauvegarde vérifiée et validation navigateur +The production checklist and remaining validation steps are tracked in +[PRODUCTION.md](PRODUCTION.md). -**Exporter les données** prépare le fichier et enregistre son empreinte complète. -**Vérifier le fichier exporté…** relit le fichier téléchargé, contrôle qu’il est -importable et compare son contenu à l’export. La date de la dernière sauvegarde -vérifiée est distincte de celle du dernier export préparé. Après sept jours sans -export vérifié, Pardis affiche un rappel pendant son utilisation. L’export reste local ; conservez le fichier hors de cet appareil. La synchronisation -facultative utilise un coffre distinct et ne remplace pas cette sauvegarde. +## Verified backups and browser validation -La purge affiche une liste figée de journées. Sauvegarde et suppression partagent -une transaction ; une correction concurrente bloque la purge et demande une -nouvelle consultation. Les autres journées récemment importées restent intactes. +**Export data (Exporter les données)** prepares a file and saves its full fingerprint. +**Verify exported file… (Vérifier le fichier exporté…)** reads the downloaded file, +checks that it can be imported, and compares its contents with the prepared export. +The date of the last verified backup is separate from the most recent prepared export. +After seven days without a verified export, Pardis shows a reminder while in use. +Export remains local; keep the file outside this device. Optional synchronization +uses a separate vault and does not replace backups. -Les tests Playwright utilisent des contextes neufs et des données synthétiques, -à 320 px. Ils vérifient consultation/correction, focus, Retour, reprise à deux -onglets, export vérifié, purge/restauration et rechargement hors ligne. Ils couvrent -aussi le nouvel écran Données, le retour du champ de conservation à sa valeur -persistée après un échec, le refus d’un export modifié et l’annulation complète -d’une purge en erreur, avec préservation de la sauvegarde précédente. Traces, -vidéos et captures sont désactivées. Pour un Chrome déjà installé, définir -`PARDIS_CHROME_PATH` avec son chemin ; sinon installer Chromium avec Playwright. -La CI installe Chromium Headless Shell et ses dépendances avant de lancer ces -tests. Elle télécharge uniquement la variante utilisée par les tests sans interface -et accorde 180 secondes à chaque connexion de téléchargement, avec les nouvelles -tentatives intégrées à Playwright. L’installation dispose de 20 minutes et le job -de validation de 30 minutes. Une erreur d’installation bloque le déploiement. +Deletion displays a fixed list of days. Backup and deletion share a transaction; a +concurrent correction blocks deletion and requires reviewing the list again. Other +recently imported days remain untouched. +Playwright tests use fresh contexts and synthetic data at 320 px. They cover +viewing/editing, focus, Back navigation, resuming in two tabs, verified export, +deletion/recovery, and offline reload. They also cover the dedicated Data screen, +restoring a retention field after a save failure, rejecting a modified export, and +fully rolling back a failed deletion while preserving the previous backup. Traces, +videos, and screenshots are disabled. To use an installed Chrome, set +`PARDIS_CHROME_PATH` to its path; otherwise install Chromium with Playwright. +CI installs Chromium Headless Shell and its dependencies before running the tests. +It downloads only the headless browser build used by the tests and gives each +download attempt 180 seconds, with Playwright's built-in retries. Browser setup has +a 20-minute limit and the validation job has a 30-minute limit. An installation +failure blocks deployment. -## Synchronisation facultative entre appareils +## Optional device synchronization -**Données et sauvegardes → Synchronisation et appareils** permet de créer un coffre -chiffré sans e-mail ni mot de passe, puis de lier des appareils avec un code temporaire -ou un QR code et une confirmation sur l’écran dédié. La saisie reste locale et -fonctionne hors ligne. Les journées concurrentes sont conservées pour résolution. -Un document de secours permet de retrouver l’accès après perte des appareils. +**Data and backups (Données et sauvegardes) → Synchronization and devices +(Synchronisation et appareils)** creates an encrypted vault without an email or +password, then links devices using a temporary code or QR code and confirmation +on the dedicated screen. Data entry stays local and works offline. Concurrent days +are retained for conflict resolution. A recovery document restores access after +losing the devices. -Les règles métier sont partagées ; le thème et le suivi des sauvegardes restent -locaux. Un appareil est responsable des automatismes, avec proposition de transfert -à l’ouverture sur un appareil secondaire. Les demandes d’association ne produisent -aucune alerte ailleurs dans l’application. +Business rules are shared; the theme and backup tracking stay local. One device is +responsible for automations, with a transfer prompt when opening the app on a +secondary device. Pairing requests do not create alerts elsewhere in the app. -Le protocole, les limites de sécurité, le fonctionnement des suppressions et les -commandes de sauvegarde/restauration du serveur sont documentés dans [SYNC.md](SYNC.md). -Pour le développement, lancer aussi `npm run sync:server` avec Node.js 24. +The protocol, security limits, deletion behavior, and server backup/restore commands +are documented in [SYNC.md](SYNC.md). For development, also run `npm run sync:server` +with Node.js 24. + +## License and font credits + +Pardis is distributed under the GNU General Public License, version 3 only +(GPL-3.0-only). See [LICENSE.md](LICENSE.md) for the full license text. The bundled font +files are separate works and remain under the SIL Open Font License, version 1.1; +their license and copyright notices are included in [public/fonts/OFL.txt](public/fonts/OFL.txt). +Copyright (C) 2026 EI Adrien Weiskopff. + +- **Bebas Neue**, by Ryoichi Tsunekawa - SIL Open Font License 1.1. +- **Charis SIL** - SIL Open Font License 1.1. See the [official Charis SIL site](https://software.sil.org/fr/charis/). diff --git a/public/fonts/BebasNeue.otf b/public/fonts/BebasNeue.otf new file mode 100644 index 0000000..214e007 Binary files /dev/null and b/public/fonts/BebasNeue.otf differ diff --git a/public/fonts/CharisSIL-R.ttf b/public/fonts/CharisSIL-R.ttf new file mode 100644 index 0000000..b8e686a Binary files /dev/null and b/public/fonts/CharisSIL-R.ttf differ diff --git a/public/fonts/OFL.txt b/public/fonts/OFL.txt new file mode 100644 index 0000000..90d37fc --- /dev/null +++ b/public/fonts/OFL.txt @@ -0,0 +1,97 @@ +Charis SIL +Copyright (c) 1997-2014, SIL International (http://www.sil.org/) +with Reserved Font Names "Charis" and "SIL". + +Bebas Neue +Copyright (c) 2010 by Ryoichi Tsunekawa. All rights reserved. + +The SIL Open Font License below applies to both fonts. + +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- +PREAMBLE + +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to provide +a free and open framework in which fonts may be shared and improved in +partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS + +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components +as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to,deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software +to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer +or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or in +the appropriate machine-readable metadata fields within text or binary +files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, must +be distributed entirely under this license, and must not be distributed +under any other license. The requirement for fonts to remain under this +license does not apply to any document created using the Font Software. + +TERMINATION + +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/App.svelte b/src/App.svelte index 6660308..ee25cc3 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -203,7 +203,7 @@ border: 0; border-radius: 0; min-height: 68px; - font-size: 0.95rem; + font-size: 1.35rem; } nav button + button { @@ -215,5 +215,5 @@ color: var(--accent-fg); } nav.with-stats { grid-template-columns: repeat(4, 1fr); } - nav.with-stats button { padding: 0 4px; font-size: 0.75rem; min-width: 0; } + nav.with-stats button { padding: 0 4px; min-width: 0; } diff --git a/src/app.css b/src/app.css index 9112bd4..703d972 100644 --- a/src/app.css +++ b/src/app.css @@ -8,26 +8,21 @@ * - Nothing tappable is under 64px; primary actions are 88px. */ -/* - * When the font files are ready, add their @font-face declarations here and - * place them under public/fonts/ (for example, /fonts/pardis-text.woff2). - * Keeping these declarations commented avoids requests for files not yet shipped. - * - * @font-face { - * font-family: 'Pardis Text'; - * src: url('/fonts/pardis-text.woff2') format('woff2'); - * font-style: normal; - * font-weight: 400; - * font-display: swap; - * } - * @font-face { - * font-family: 'Pardis Headings'; - * src: url('/fonts/pardis-headings.woff2') format('woff2'); - * font-style: normal; - * font-weight: 400; - * font-display: swap; - * } - */ +@font-face { + font-family: 'Pardis Text'; + src: url('/fonts/CharisSIL-R.ttf') format('truetype'); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: 'Pardis Headings'; + src: url('/fonts/BebasNeue.otf') format('opentype'); + font-style: normal; + font-weight: 400; + font-display: swap; +} :root { --bg: #ffffff; @@ -113,8 +108,8 @@ h2, h3 { font-family: var(--font-heading); margin: 0; - font-weight: 800; - letter-spacing: -0.01em; + font-weight: 400; + letter-spacing: 0.0em; } h1 { font-size: 1.6rem; @@ -128,6 +123,9 @@ h3 { p { margin: 0; } +a, a:visited { + color: var(--accent); +} /* Times and durations must never reflow as digits change. */ .tnum { @@ -145,8 +143,9 @@ p { /* ------------------------------------------------------------------ controls */ button { - font: inherit; - font-weight: 700; + font-family: var(--font-heading); + font-weight: 400; + font-size: 1.2rem; color: var(--fg); background: var(--bg); border: var(--border) solid var(--line); diff --git a/src/routes/About.svelte b/src/routes/About.svelte index e28c864..20dd9e7 100644 --- a/src/routes/About.svelte +++ b/src/routes/About.svelte @@ -10,6 +10,8 @@ enregistre les journées, les tâches, les clients, les repas et les pauses, puis prépare un récapitulatif à consulter ou à copier.

+

L'application et le service de synchronisation sont fournis gratuitement et + sans aucune garantie de disponibilité ou de continuité.

Fonctionnement

@@ -49,30 +51,43 @@ publiques des appareils, liens entre révisions, tailles et dates de connexion. Ces données sont conservées un an, conformément à la loi.

-

- Supprimer une journée de l’historique synchronisé ne supprime pas ses - anciennes révisions chiffrées du coffre. Elles y restent jusqu’à la - suppression du coffre. Les sauvegardes de l’hébergement peuvent aussi - conserver des données supprimées jusqu’à l’expiration de leur rétention. -

Opérateur de cette instance

- Le service de synchronisation de cette instance est hébergé sur un serveur - sauvegardé quotidiennement. Les sauvegardes de ce serveur - protègent le service en ligne ; elles ne sauvegardent pas les données - conservées uniquement dans le navigateur de chaque utilisateur. -

-

- L'application est développée par Adrien Weiskopff. + L'application est développée par EI Adrien Weiskopff - clawd.fr. L'application et son serveur de production sont hébergés en France par - clawd.fr (EI Adrien Weiskopff). - Pour plus d'informations rendez-vous sur le site https://clawd.fr ou écrivez - à l'adresse pardis@clawd.fr. + EI Adrien Weiskopff - clawd.fr. + Pour plus d'informations rendez-vous sur le site https://clawd.fr + ou écrivez à l'adresse pardis@clawd.fr.

+ +
+

Licence et crédits typographiques

+

+ Le code source de Pardis est distribué sous la licence GNU General Public + License, version 3 uniquement (GPL-3.0-only). Consultez le + texte de la GPL v3. + Les fichiers de polices sont des œuvres distinctes du code de l’application + et restent sous leur propre licence, la SIL Open Font License, version 1.1. + Le texte de l’OFL et les mentions de droits d’auteur accompagnent les fichiers + dans la distribution du projet. +

+

Copyright (C) 2026 EI Adrien Weiskopff.

+
    +
  • Bebas Neue, par Ryoichi Tsunekawa — SIL Open Font License 1.1.
  • +
  • + Charis SIL — SIL Open Font License 1.1. Voir le + site officiel de Charis SIL. +
  • +
+

+ Texte officiel de la SIL Open Font License 1.1. +

+
+