├── .gitignore ├── .husky └── pre-commit ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── biome.json ├── eslint.config.js ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public └── fsm-engine.svg ├── src ├── App.tsx ├── assets │ └── github_font.ttf ├── components │ ├── Alert.tsx │ ├── Dock.tsx │ ├── Editor.tsx │ ├── Error.tsx │ ├── Settings.tsx │ └── Welcome.tsx ├── index.css ├── lib │ ├── backend.ts │ └── content.ts ├── main.tsx └── vite-env.d.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm build 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First off, thanks for taking the time to contribute! 🎉 4 | 5 | This is a small project, and contributions are always welcome. 6 | 7 | ## How to Contribute 8 | 9 | If you have a bug fix or a new feature you'd like to add, here's how you can do it: 10 | 11 | 1. **Fork the repository** on GitHub. 12 | 2. **Create a new branch** for your changes. Something like `fix/your-bug-fix` or `feature/your-new-feature`. 13 | 3. **Make your changes** in your branch. 14 | 4. **Submit a Pull Request** to the `dev` branch of this repository. 15 | 16 | ## Pull Requests 17 | 18 | When you submit a pull request, please include a clear description of your changes. 19 | 20 | - What does this PR do? 21 | - Why is this change needed? 22 | - If it fixes an issue, please link to the issue. 23 | 24 | ## Coding Style 25 | 26 | There are no strict coding style guidelines. Just try to match the style of the existing code. The goal is to keep the code readable and maintainable. 27 | 28 | That's it! Thanks for your contribution. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FSM Engine 2 | 3 | A web-based tool for creating, and visualizing, Finite State Machines (FSMs). Built with React, TypeScript, Tailwind CSS, Jotai, and React Konva for an interactive canvas experience. 4 | 5 | FSM Engine Demo 6 | 7 | ## Features 8 | 9 | - Interactive Canvas Editor 10 | - Zoom and Pan across an infinite canvas 11 | - Smooth drag to reposition states 12 | - Multiple Modes 13 | - Create: Click on the canvas to add new states 14 | - Select: Drag states to move them 15 | - Connect: Click two states to create a directed transition (supports self-loops) 16 | - Delete: Remove states with a single click 17 | - Grab: Move the Nodes 18 | - State Types 19 | - initial, intermediate, final 20 | - Dynamic Transitions 21 | - Arrows automatically adjust their position and curve as you move states 22 | - Welcome/Tutorial Overlay 23 | - First-run walkthrough with short clips 24 | 25 | ## Try it at 26 | https://fsm-engine.vercel.app 27 | 28 | ## Tech Stack 29 | 30 | - Frontend: React + TypeScript 31 | - State Management: Jotai 32 | - Canvas: React Konva 33 | - Styling: Tailwind CSS 34 | - Tooling: Vite 35 | - Icons: lucide-react 36 | 37 | ## Contributing 38 | 39 | Contributions are welcome! 40 | - Open issues for bugs or feature requests 41 | - Submit pull requests for fixes or enhancements 42 | - Discuss larger changes in an issue first 43 | 44 | ## Roadmap 45 | - [ ] Undo/Redo 46 | - [ ] Export Project to JSON 47 | - [ ] Import Project from JSON 48 | - [ ] Generate the transition table for a given NFA/DFA 49 | - [ ] NFA → DFA conversion 50 | - [ ] DFA minimization 51 | - [ ] Validation and error hints (unreachable states, parse a regex) 52 | - [ ] Keyboard shortcuts and accessibility improvements 53 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": true, 10 | "includes": ["**", "!node_modules", "!.next", "!dist", "!build"] 11 | }, 12 | "formatter": { 13 | "enabled": true, 14 | "indentStyle": "tab" 15 | }, 16 | "linter": { 17 | "enabled": true, 18 | "rules": { 19 | "recommended": true 20 | } 21 | }, 22 | "javascript": { 23 | "formatter": { 24 | "quoteStyle": "double" 25 | } 26 | }, 27 | "assist": { 28 | "enabled": true, 29 | "actions": { 30 | "source": { 31 | "organizeImports": "on" 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from "@eslint/js"; 2 | import globals from "globals"; 3 | import reactHooks from "eslint-plugin-react-hooks"; 4 | import reactRefresh from "eslint-plugin-react-refresh"; 5 | import tseslint from "typescript-eslint"; 6 | import { globalIgnores } from "eslint/config"; 7 | 8 | export default tseslint.config([ 9 | globalIgnores(["dist"]), 10 | { 11 | files: ["**/*.{ts,tsx}"], 12 | extends: [ 13 | js.configs.recommended, 14 | tseslint.configs.recommended, 15 | reactHooks.configs["recommended-latest"], 16 | reactRefresh.configs.vite, 17 | ], 18 | languageOptions: { 19 | ecmaVersion: 2020, 20 | globals: globals.browser, 21 | }, 22 | }, 23 | ]); 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | FSM Engine 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fsm-engine", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc -b && vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview", 11 | "prepare": "husky", 12 | "format": "biome format --write" 13 | }, 14 | "dependencies": { 15 | "@tailwindcss/vite": "^4.1.13", 16 | "clsx": "^2.1.1", 17 | "jotai": "^2.14.0", 18 | "konva": "^9.3.22", 19 | "lucide-react": "^0.542.0", 20 | "react": "^19.1.1", 21 | "react-dom": "^19.1.1", 22 | "react-gridlines": "^1.1.7", 23 | "react-konva": "^19.0.10", 24 | "tailwindcss": "^4.1.13" 25 | }, 26 | "devDependencies": { 27 | "@biomejs/biome": "2.2.4", 28 | "@eslint/js": "^9.35.0", 29 | "@types/react": "^19.1.12", 30 | "@types/react-dom": "^19.1.9", 31 | "@vitejs/plugin-react-swc": "^4.0.1", 32 | "eslint": "^9.35.0", 33 | "eslint-plugin-react-hooks": "^5.2.0", 34 | "eslint-plugin-react-refresh": "^0.4.20", 35 | "globals": "^16.4.0", 36 | "husky": "^9.1.7", 37 | "typescript": "~5.8.3", 38 | "typescript-eslint": "^8.43.0", 39 | "vite": "^7.1.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@tailwindcss/vite': 12 | specifier: ^4.1.13 13 | version: 4.1.13(vite@7.1.5(jiti@2.5.1)(lightningcss@1.30.1)) 14 | clsx: 15 | specifier: ^2.1.1 16 | version: 2.1.1 17 | jotai: 18 | specifier: ^2.14.0 19 | version: 2.14.0(@types/react@19.1.12)(react@19.1.1) 20 | konva: 21 | specifier: ^9.3.22 22 | version: 9.3.22 23 | lucide-react: 24 | specifier: ^0.542.0 25 | version: 0.542.0(react@19.1.1) 26 | react: 27 | specifier: ^19.1.1 28 | version: 19.1.1 29 | react-dom: 30 | specifier: ^19.1.1 31 | version: 19.1.1(react@19.1.1) 32 | react-gridlines: 33 | specifier: ^1.1.7 34 | version: 1.1.7(react@19.1.1) 35 | react-konva: 36 | specifier: ^19.0.10 37 | version: 19.0.10(@types/react@19.1.12)(konva@9.3.22)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) 38 | tailwindcss: 39 | specifier: ^4.1.13 40 | version: 4.1.13 41 | devDependencies: 42 | '@biomejs/biome': 43 | specifier: 2.2.4 44 | version: 2.2.4 45 | '@eslint/js': 46 | specifier: ^9.35.0 47 | version: 9.35.0 48 | '@types/react': 49 | specifier: ^19.1.12 50 | version: 19.1.12 51 | '@types/react-dom': 52 | specifier: ^19.1.9 53 | version: 19.1.9(@types/react@19.1.12) 54 | '@vitejs/plugin-react-swc': 55 | specifier: ^4.0.1 56 | version: 4.0.1(vite@7.1.5(jiti@2.5.1)(lightningcss@1.30.1)) 57 | eslint: 58 | specifier: ^9.35.0 59 | version: 9.35.0(jiti@2.5.1) 60 | eslint-plugin-react-hooks: 61 | specifier: ^5.2.0 62 | version: 5.2.0(eslint@9.35.0(jiti@2.5.1)) 63 | eslint-plugin-react-refresh: 64 | specifier: ^0.4.20 65 | version: 0.4.20(eslint@9.35.0(jiti@2.5.1)) 66 | globals: 67 | specifier: ^16.4.0 68 | version: 16.4.0 69 | husky: 70 | specifier: ^9.1.7 71 | version: 9.1.7 72 | typescript: 73 | specifier: ~5.8.3 74 | version: 5.8.3 75 | typescript-eslint: 76 | specifier: ^8.43.0 77 | version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3) 78 | vite: 79 | specifier: ^7.1.5 80 | version: 7.1.5(jiti@2.5.1)(lightningcss@1.30.1) 81 | 82 | packages: 83 | 84 | '@biomejs/biome@2.2.4': 85 | resolution: {integrity: sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==} 86 | engines: {node: '>=14.21.3'} 87 | hasBin: true 88 | 89 | '@biomejs/cli-darwin-arm64@2.2.4': 90 | resolution: {integrity: sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==} 91 | engines: {node: '>=14.21.3'} 92 | cpu: [arm64] 93 | os: [darwin] 94 | 95 | '@biomejs/cli-darwin-x64@2.2.4': 96 | resolution: {integrity: sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==} 97 | engines: {node: '>=14.21.3'} 98 | cpu: [x64] 99 | os: [darwin] 100 | 101 | '@biomejs/cli-linux-arm64-musl@2.2.4': 102 | resolution: {integrity: sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==} 103 | engines: {node: '>=14.21.3'} 104 | cpu: [arm64] 105 | os: [linux] 106 | 107 | '@biomejs/cli-linux-arm64@2.2.4': 108 | resolution: {integrity: sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==} 109 | engines: {node: '>=14.21.3'} 110 | cpu: [arm64] 111 | os: [linux] 112 | 113 | '@biomejs/cli-linux-x64-musl@2.2.4': 114 | resolution: {integrity: sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==} 115 | engines: {node: '>=14.21.3'} 116 | cpu: [x64] 117 | os: [linux] 118 | 119 | '@biomejs/cli-linux-x64@2.2.4': 120 | resolution: {integrity: sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==} 121 | engines: {node: '>=14.21.3'} 122 | cpu: [x64] 123 | os: [linux] 124 | 125 | '@biomejs/cli-win32-arm64@2.2.4': 126 | resolution: {integrity: sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==} 127 | engines: {node: '>=14.21.3'} 128 | cpu: [arm64] 129 | os: [win32] 130 | 131 | '@biomejs/cli-win32-x64@2.2.4': 132 | resolution: {integrity: sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==} 133 | engines: {node: '>=14.21.3'} 134 | cpu: [x64] 135 | os: [win32] 136 | 137 | '@esbuild/aix-ppc64@0.25.9': 138 | resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} 139 | engines: {node: '>=18'} 140 | cpu: [ppc64] 141 | os: [aix] 142 | 143 | '@esbuild/android-arm64@0.25.9': 144 | resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} 145 | engines: {node: '>=18'} 146 | cpu: [arm64] 147 | os: [android] 148 | 149 | '@esbuild/android-arm@0.25.9': 150 | resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} 151 | engines: {node: '>=18'} 152 | cpu: [arm] 153 | os: [android] 154 | 155 | '@esbuild/android-x64@0.25.9': 156 | resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} 157 | engines: {node: '>=18'} 158 | cpu: [x64] 159 | os: [android] 160 | 161 | '@esbuild/darwin-arm64@0.25.9': 162 | resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} 163 | engines: {node: '>=18'} 164 | cpu: [arm64] 165 | os: [darwin] 166 | 167 | '@esbuild/darwin-x64@0.25.9': 168 | resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} 169 | engines: {node: '>=18'} 170 | cpu: [x64] 171 | os: [darwin] 172 | 173 | '@esbuild/freebsd-arm64@0.25.9': 174 | resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} 175 | engines: {node: '>=18'} 176 | cpu: [arm64] 177 | os: [freebsd] 178 | 179 | '@esbuild/freebsd-x64@0.25.9': 180 | resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} 181 | engines: {node: '>=18'} 182 | cpu: [x64] 183 | os: [freebsd] 184 | 185 | '@esbuild/linux-arm64@0.25.9': 186 | resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} 187 | engines: {node: '>=18'} 188 | cpu: [arm64] 189 | os: [linux] 190 | 191 | '@esbuild/linux-arm@0.25.9': 192 | resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} 193 | engines: {node: '>=18'} 194 | cpu: [arm] 195 | os: [linux] 196 | 197 | '@esbuild/linux-ia32@0.25.9': 198 | resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} 199 | engines: {node: '>=18'} 200 | cpu: [ia32] 201 | os: [linux] 202 | 203 | '@esbuild/linux-loong64@0.25.9': 204 | resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} 205 | engines: {node: '>=18'} 206 | cpu: [loong64] 207 | os: [linux] 208 | 209 | '@esbuild/linux-mips64el@0.25.9': 210 | resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} 211 | engines: {node: '>=18'} 212 | cpu: [mips64el] 213 | os: [linux] 214 | 215 | '@esbuild/linux-ppc64@0.25.9': 216 | resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} 217 | engines: {node: '>=18'} 218 | cpu: [ppc64] 219 | os: [linux] 220 | 221 | '@esbuild/linux-riscv64@0.25.9': 222 | resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} 223 | engines: {node: '>=18'} 224 | cpu: [riscv64] 225 | os: [linux] 226 | 227 | '@esbuild/linux-s390x@0.25.9': 228 | resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} 229 | engines: {node: '>=18'} 230 | cpu: [s390x] 231 | os: [linux] 232 | 233 | '@esbuild/linux-x64@0.25.9': 234 | resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} 235 | engines: {node: '>=18'} 236 | cpu: [x64] 237 | os: [linux] 238 | 239 | '@esbuild/netbsd-arm64@0.25.9': 240 | resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} 241 | engines: {node: '>=18'} 242 | cpu: [arm64] 243 | os: [netbsd] 244 | 245 | '@esbuild/netbsd-x64@0.25.9': 246 | resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} 247 | engines: {node: '>=18'} 248 | cpu: [x64] 249 | os: [netbsd] 250 | 251 | '@esbuild/openbsd-arm64@0.25.9': 252 | resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} 253 | engines: {node: '>=18'} 254 | cpu: [arm64] 255 | os: [openbsd] 256 | 257 | '@esbuild/openbsd-x64@0.25.9': 258 | resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} 259 | engines: {node: '>=18'} 260 | cpu: [x64] 261 | os: [openbsd] 262 | 263 | '@esbuild/openharmony-arm64@0.25.9': 264 | resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} 265 | engines: {node: '>=18'} 266 | cpu: [arm64] 267 | os: [openharmony] 268 | 269 | '@esbuild/sunos-x64@0.25.9': 270 | resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} 271 | engines: {node: '>=18'} 272 | cpu: [x64] 273 | os: [sunos] 274 | 275 | '@esbuild/win32-arm64@0.25.9': 276 | resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} 277 | engines: {node: '>=18'} 278 | cpu: [arm64] 279 | os: [win32] 280 | 281 | '@esbuild/win32-ia32@0.25.9': 282 | resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} 283 | engines: {node: '>=18'} 284 | cpu: [ia32] 285 | os: [win32] 286 | 287 | '@esbuild/win32-x64@0.25.9': 288 | resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} 289 | engines: {node: '>=18'} 290 | cpu: [x64] 291 | os: [win32] 292 | 293 | '@eslint-community/eslint-utils@4.9.0': 294 | resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} 295 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 296 | peerDependencies: 297 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 298 | 299 | '@eslint-community/regexpp@4.12.1': 300 | resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} 301 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 302 | 303 | '@eslint/config-array@0.21.0': 304 | resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} 305 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 306 | 307 | '@eslint/config-helpers@0.3.1': 308 | resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} 309 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 310 | 311 | '@eslint/core@0.15.2': 312 | resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} 313 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 314 | 315 | '@eslint/eslintrc@3.3.1': 316 | resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} 317 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 318 | 319 | '@eslint/js@9.35.0': 320 | resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==} 321 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 322 | 323 | '@eslint/object-schema@2.1.6': 324 | resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} 325 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 326 | 327 | '@eslint/plugin-kit@0.3.5': 328 | resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} 329 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 330 | 331 | '@humanfs/core@0.19.1': 332 | resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 333 | engines: {node: '>=18.18.0'} 334 | 335 | '@humanfs/node@0.16.7': 336 | resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} 337 | engines: {node: '>=18.18.0'} 338 | 339 | '@humanwhocodes/module-importer@1.0.1': 340 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 341 | engines: {node: '>=12.22'} 342 | 343 | '@humanwhocodes/retry@0.4.3': 344 | resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 345 | engines: {node: '>=18.18'} 346 | 347 | '@isaacs/fs-minipass@4.0.1': 348 | resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} 349 | engines: {node: '>=18.0.0'} 350 | 351 | '@jridgewell/gen-mapping@0.3.13': 352 | resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} 353 | 354 | '@jridgewell/remapping@2.3.5': 355 | resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} 356 | 357 | '@jridgewell/resolve-uri@3.1.2': 358 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 359 | engines: {node: '>=6.0.0'} 360 | 361 | '@jridgewell/sourcemap-codec@1.5.5': 362 | resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} 363 | 364 | '@jridgewell/trace-mapping@0.3.31': 365 | resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} 366 | 367 | '@nodelib/fs.scandir@2.1.5': 368 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 369 | engines: {node: '>= 8'} 370 | 371 | '@nodelib/fs.stat@2.0.5': 372 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 373 | engines: {node: '>= 8'} 374 | 375 | '@nodelib/fs.walk@1.2.8': 376 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 377 | engines: {node: '>= 8'} 378 | 379 | '@rolldown/pluginutils@1.0.0-beta.32': 380 | resolution: {integrity: sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==} 381 | 382 | '@rollup/rollup-android-arm-eabi@4.50.1': 383 | resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} 384 | cpu: [arm] 385 | os: [android] 386 | 387 | '@rollup/rollup-android-arm64@4.50.1': 388 | resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} 389 | cpu: [arm64] 390 | os: [android] 391 | 392 | '@rollup/rollup-darwin-arm64@4.50.1': 393 | resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} 394 | cpu: [arm64] 395 | os: [darwin] 396 | 397 | '@rollup/rollup-darwin-x64@4.50.1': 398 | resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} 399 | cpu: [x64] 400 | os: [darwin] 401 | 402 | '@rollup/rollup-freebsd-arm64@4.50.1': 403 | resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} 404 | cpu: [arm64] 405 | os: [freebsd] 406 | 407 | '@rollup/rollup-freebsd-x64@4.50.1': 408 | resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} 409 | cpu: [x64] 410 | os: [freebsd] 411 | 412 | '@rollup/rollup-linux-arm-gnueabihf@4.50.1': 413 | resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} 414 | cpu: [arm] 415 | os: [linux] 416 | 417 | '@rollup/rollup-linux-arm-musleabihf@4.50.1': 418 | resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} 419 | cpu: [arm] 420 | os: [linux] 421 | 422 | '@rollup/rollup-linux-arm64-gnu@4.50.1': 423 | resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} 424 | cpu: [arm64] 425 | os: [linux] 426 | 427 | '@rollup/rollup-linux-arm64-musl@4.50.1': 428 | resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} 429 | cpu: [arm64] 430 | os: [linux] 431 | 432 | '@rollup/rollup-linux-loongarch64-gnu@4.50.1': 433 | resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} 434 | cpu: [loong64] 435 | os: [linux] 436 | 437 | '@rollup/rollup-linux-ppc64-gnu@4.50.1': 438 | resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} 439 | cpu: [ppc64] 440 | os: [linux] 441 | 442 | '@rollup/rollup-linux-riscv64-gnu@4.50.1': 443 | resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} 444 | cpu: [riscv64] 445 | os: [linux] 446 | 447 | '@rollup/rollup-linux-riscv64-musl@4.50.1': 448 | resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} 449 | cpu: [riscv64] 450 | os: [linux] 451 | 452 | '@rollup/rollup-linux-s390x-gnu@4.50.1': 453 | resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} 454 | cpu: [s390x] 455 | os: [linux] 456 | 457 | '@rollup/rollup-linux-x64-gnu@4.50.1': 458 | resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} 459 | cpu: [x64] 460 | os: [linux] 461 | 462 | '@rollup/rollup-linux-x64-musl@4.50.1': 463 | resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} 464 | cpu: [x64] 465 | os: [linux] 466 | 467 | '@rollup/rollup-openharmony-arm64@4.50.1': 468 | resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} 469 | cpu: [arm64] 470 | os: [openharmony] 471 | 472 | '@rollup/rollup-win32-arm64-msvc@4.50.1': 473 | resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} 474 | cpu: [arm64] 475 | os: [win32] 476 | 477 | '@rollup/rollup-win32-ia32-msvc@4.50.1': 478 | resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} 479 | cpu: [ia32] 480 | os: [win32] 481 | 482 | '@rollup/rollup-win32-x64-msvc@4.50.1': 483 | resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} 484 | cpu: [x64] 485 | os: [win32] 486 | 487 | '@swc/core-darwin-arm64@1.13.5': 488 | resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} 489 | engines: {node: '>=10'} 490 | cpu: [arm64] 491 | os: [darwin] 492 | 493 | '@swc/core-darwin-x64@1.13.5': 494 | resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==} 495 | engines: {node: '>=10'} 496 | cpu: [x64] 497 | os: [darwin] 498 | 499 | '@swc/core-linux-arm-gnueabihf@1.13.5': 500 | resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==} 501 | engines: {node: '>=10'} 502 | cpu: [arm] 503 | os: [linux] 504 | 505 | '@swc/core-linux-arm64-gnu@1.13.5': 506 | resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==} 507 | engines: {node: '>=10'} 508 | cpu: [arm64] 509 | os: [linux] 510 | 511 | '@swc/core-linux-arm64-musl@1.13.5': 512 | resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} 513 | engines: {node: '>=10'} 514 | cpu: [arm64] 515 | os: [linux] 516 | 517 | '@swc/core-linux-x64-gnu@1.13.5': 518 | resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} 519 | engines: {node: '>=10'} 520 | cpu: [x64] 521 | os: [linux] 522 | 523 | '@swc/core-linux-x64-musl@1.13.5': 524 | resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} 525 | engines: {node: '>=10'} 526 | cpu: [x64] 527 | os: [linux] 528 | 529 | '@swc/core-win32-arm64-msvc@1.13.5': 530 | resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} 531 | engines: {node: '>=10'} 532 | cpu: [arm64] 533 | os: [win32] 534 | 535 | '@swc/core-win32-ia32-msvc@1.13.5': 536 | resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==} 537 | engines: {node: '>=10'} 538 | cpu: [ia32] 539 | os: [win32] 540 | 541 | '@swc/core-win32-x64-msvc@1.13.5': 542 | resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==} 543 | engines: {node: '>=10'} 544 | cpu: [x64] 545 | os: [win32] 546 | 547 | '@swc/core@1.13.5': 548 | resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==} 549 | engines: {node: '>=10'} 550 | peerDependencies: 551 | '@swc/helpers': '>=0.5.17' 552 | peerDependenciesMeta: 553 | '@swc/helpers': 554 | optional: true 555 | 556 | '@swc/counter@0.1.3': 557 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 558 | 559 | '@swc/types@0.1.25': 560 | resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} 561 | 562 | '@tailwindcss/node@4.1.13': 563 | resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} 564 | 565 | '@tailwindcss/oxide-android-arm64@4.1.13': 566 | resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} 567 | engines: {node: '>= 10'} 568 | cpu: [arm64] 569 | os: [android] 570 | 571 | '@tailwindcss/oxide-darwin-arm64@4.1.13': 572 | resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} 573 | engines: {node: '>= 10'} 574 | cpu: [arm64] 575 | os: [darwin] 576 | 577 | '@tailwindcss/oxide-darwin-x64@4.1.13': 578 | resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} 579 | engines: {node: '>= 10'} 580 | cpu: [x64] 581 | os: [darwin] 582 | 583 | '@tailwindcss/oxide-freebsd-x64@4.1.13': 584 | resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} 585 | engines: {node: '>= 10'} 586 | cpu: [x64] 587 | os: [freebsd] 588 | 589 | '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': 590 | resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} 591 | engines: {node: '>= 10'} 592 | cpu: [arm] 593 | os: [linux] 594 | 595 | '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': 596 | resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} 597 | engines: {node: '>= 10'} 598 | cpu: [arm64] 599 | os: [linux] 600 | 601 | '@tailwindcss/oxide-linux-arm64-musl@4.1.13': 602 | resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} 603 | engines: {node: '>= 10'} 604 | cpu: [arm64] 605 | os: [linux] 606 | 607 | '@tailwindcss/oxide-linux-x64-gnu@4.1.13': 608 | resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} 609 | engines: {node: '>= 10'} 610 | cpu: [x64] 611 | os: [linux] 612 | 613 | '@tailwindcss/oxide-linux-x64-musl@4.1.13': 614 | resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} 615 | engines: {node: '>= 10'} 616 | cpu: [x64] 617 | os: [linux] 618 | 619 | '@tailwindcss/oxide-wasm32-wasi@4.1.13': 620 | resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} 621 | engines: {node: '>=14.0.0'} 622 | cpu: [wasm32] 623 | bundledDependencies: 624 | - '@napi-rs/wasm-runtime' 625 | - '@emnapi/core' 626 | - '@emnapi/runtime' 627 | - '@tybys/wasm-util' 628 | - '@emnapi/wasi-threads' 629 | - tslib 630 | 631 | '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': 632 | resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} 633 | engines: {node: '>= 10'} 634 | cpu: [arm64] 635 | os: [win32] 636 | 637 | '@tailwindcss/oxide-win32-x64-msvc@4.1.13': 638 | resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} 639 | engines: {node: '>= 10'} 640 | cpu: [x64] 641 | os: [win32] 642 | 643 | '@tailwindcss/oxide@4.1.13': 644 | resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} 645 | engines: {node: '>= 10'} 646 | 647 | '@tailwindcss/vite@4.1.13': 648 | resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==} 649 | peerDependencies: 650 | vite: ^5.2.0 || ^6 || ^7 651 | 652 | '@types/estree@1.0.8': 653 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 654 | 655 | '@types/json-schema@7.0.15': 656 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 657 | 658 | '@types/react-dom@19.1.9': 659 | resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} 660 | peerDependencies: 661 | '@types/react': ^19.0.0 662 | 663 | '@types/react-reconciler@0.28.9': 664 | resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==} 665 | peerDependencies: 666 | '@types/react': '*' 667 | 668 | '@types/react-reconciler@0.32.1': 669 | resolution: {integrity: sha512-RsqPttsBQ+6af0nATFXJJpemYQH7kL9+xLNm1z+0MjQFDKBZDM2R6SBrjdvRmHu9i9fM6povACj57Ft+pKRNOA==} 670 | peerDependencies: 671 | '@types/react': '*' 672 | 673 | '@types/react@19.1.12': 674 | resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==} 675 | 676 | '@typescript-eslint/eslint-plugin@8.43.0': 677 | resolution: {integrity: sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==} 678 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 679 | peerDependencies: 680 | '@typescript-eslint/parser': ^8.43.0 681 | eslint: ^8.57.0 || ^9.0.0 682 | typescript: '>=4.8.4 <6.0.0' 683 | 684 | '@typescript-eslint/parser@8.43.0': 685 | resolution: {integrity: sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==} 686 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 687 | peerDependencies: 688 | eslint: ^8.57.0 || ^9.0.0 689 | typescript: '>=4.8.4 <6.0.0' 690 | 691 | '@typescript-eslint/project-service@8.43.0': 692 | resolution: {integrity: sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==} 693 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 694 | peerDependencies: 695 | typescript: '>=4.8.4 <6.0.0' 696 | 697 | '@typescript-eslint/scope-manager@8.43.0': 698 | resolution: {integrity: sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==} 699 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 700 | 701 | '@typescript-eslint/tsconfig-utils@8.43.0': 702 | resolution: {integrity: sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==} 703 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 704 | peerDependencies: 705 | typescript: '>=4.8.4 <6.0.0' 706 | 707 | '@typescript-eslint/type-utils@8.43.0': 708 | resolution: {integrity: sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==} 709 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 710 | peerDependencies: 711 | eslint: ^8.57.0 || ^9.0.0 712 | typescript: '>=4.8.4 <6.0.0' 713 | 714 | '@typescript-eslint/types@8.43.0': 715 | resolution: {integrity: sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==} 716 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 717 | 718 | '@typescript-eslint/typescript-estree@8.43.0': 719 | resolution: {integrity: sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==} 720 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 721 | peerDependencies: 722 | typescript: '>=4.8.4 <6.0.0' 723 | 724 | '@typescript-eslint/utils@8.43.0': 725 | resolution: {integrity: sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==} 726 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 727 | peerDependencies: 728 | eslint: ^8.57.0 || ^9.0.0 729 | typescript: '>=4.8.4 <6.0.0' 730 | 731 | '@typescript-eslint/visitor-keys@8.43.0': 732 | resolution: {integrity: sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==} 733 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 734 | 735 | '@vitejs/plugin-react-swc@4.0.1': 736 | resolution: {integrity: sha512-NQhPjysi5duItyrMd5JWZFf2vNOuSMyw+EoZyTBDzk+DkfYD8WNrsUs09sELV2cr1P15nufsN25hsUBt4CKF9Q==} 737 | engines: {node: ^20.19.0 || >=22.12.0} 738 | peerDependencies: 739 | vite: ^4 || ^5 || ^6 || ^7 740 | 741 | acorn-jsx@5.3.2: 742 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 743 | peerDependencies: 744 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 745 | 746 | acorn@8.15.0: 747 | resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 748 | engines: {node: '>=0.4.0'} 749 | hasBin: true 750 | 751 | ajv@6.12.6: 752 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 753 | 754 | ansi-styles@4.3.0: 755 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 756 | engines: {node: '>=8'} 757 | 758 | argparse@2.0.1: 759 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 760 | 761 | balanced-match@1.0.2: 762 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 763 | 764 | brace-expansion@1.1.12: 765 | resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} 766 | 767 | brace-expansion@2.0.2: 768 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 769 | 770 | braces@3.0.3: 771 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 772 | engines: {node: '>=8'} 773 | 774 | callsites@3.1.0: 775 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 776 | engines: {node: '>=6'} 777 | 778 | chalk@4.1.2: 779 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 780 | engines: {node: '>=10'} 781 | 782 | chownr@3.0.0: 783 | resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} 784 | engines: {node: '>=18'} 785 | 786 | clsx@2.1.1: 787 | resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} 788 | engines: {node: '>=6'} 789 | 790 | color-convert@2.0.1: 791 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 792 | engines: {node: '>=7.0.0'} 793 | 794 | color-name@1.1.4: 795 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 796 | 797 | concat-map@0.0.1: 798 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 799 | 800 | cross-spawn@7.0.6: 801 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 802 | engines: {node: '>= 8'} 803 | 804 | csstype@3.1.3: 805 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 806 | 807 | debug@4.4.1: 808 | resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} 809 | engines: {node: '>=6.0'} 810 | peerDependencies: 811 | supports-color: '*' 812 | peerDependenciesMeta: 813 | supports-color: 814 | optional: true 815 | 816 | deep-is@0.1.4: 817 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 818 | 819 | detect-libc@2.0.4: 820 | resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} 821 | engines: {node: '>=8'} 822 | 823 | enhanced-resolve@5.18.3: 824 | resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} 825 | engines: {node: '>=10.13.0'} 826 | 827 | esbuild@0.25.9: 828 | resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} 829 | engines: {node: '>=18'} 830 | hasBin: true 831 | 832 | escape-string-regexp@4.0.0: 833 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 834 | engines: {node: '>=10'} 835 | 836 | eslint-plugin-react-hooks@5.2.0: 837 | resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} 838 | engines: {node: '>=10'} 839 | peerDependencies: 840 | eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 841 | 842 | eslint-plugin-react-refresh@0.4.20: 843 | resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} 844 | peerDependencies: 845 | eslint: '>=8.40' 846 | 847 | eslint-scope@8.4.0: 848 | resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 849 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 850 | 851 | eslint-visitor-keys@3.4.3: 852 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 853 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 854 | 855 | eslint-visitor-keys@4.2.1: 856 | resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 857 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 858 | 859 | eslint@9.35.0: 860 | resolution: {integrity: sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==} 861 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 862 | hasBin: true 863 | peerDependencies: 864 | jiti: '*' 865 | peerDependenciesMeta: 866 | jiti: 867 | optional: true 868 | 869 | espree@10.4.0: 870 | resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 871 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 872 | 873 | esquery@1.6.0: 874 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 875 | engines: {node: '>=0.10'} 876 | 877 | esrecurse@4.3.0: 878 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 879 | engines: {node: '>=4.0'} 880 | 881 | estraverse@5.3.0: 882 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 883 | engines: {node: '>=4.0'} 884 | 885 | esutils@2.0.3: 886 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 887 | engines: {node: '>=0.10.0'} 888 | 889 | fast-deep-equal@3.1.3: 890 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 891 | 892 | fast-glob@3.3.3: 893 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 894 | engines: {node: '>=8.6.0'} 895 | 896 | fast-json-stable-stringify@2.1.0: 897 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 898 | 899 | fast-levenshtein@2.0.6: 900 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 901 | 902 | fastq@1.19.1: 903 | resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 904 | 905 | fdir@6.5.0: 906 | resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 907 | engines: {node: '>=12.0.0'} 908 | peerDependencies: 909 | picomatch: ^3 || ^4 910 | peerDependenciesMeta: 911 | picomatch: 912 | optional: true 913 | 914 | file-entry-cache@8.0.0: 915 | resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 916 | engines: {node: '>=16.0.0'} 917 | 918 | fill-range@7.1.1: 919 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 920 | engines: {node: '>=8'} 921 | 922 | find-up@5.0.0: 923 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 924 | engines: {node: '>=10'} 925 | 926 | flat-cache@4.0.1: 927 | resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 928 | engines: {node: '>=16'} 929 | 930 | flatted@3.3.3: 931 | resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 932 | 933 | fsevents@2.3.3: 934 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 935 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 936 | os: [darwin] 937 | 938 | glob-parent@5.1.2: 939 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 940 | engines: {node: '>= 6'} 941 | 942 | glob-parent@6.0.2: 943 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 944 | engines: {node: '>=10.13.0'} 945 | 946 | globals@14.0.0: 947 | resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 948 | engines: {node: '>=18'} 949 | 950 | globals@16.4.0: 951 | resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} 952 | engines: {node: '>=18'} 953 | 954 | graceful-fs@4.2.11: 955 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 956 | 957 | graphemer@1.4.0: 958 | resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 959 | 960 | has-flag@4.0.0: 961 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 962 | engines: {node: '>=8'} 963 | 964 | husky@9.1.7: 965 | resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} 966 | engines: {node: '>=18'} 967 | hasBin: true 968 | 969 | ignore@5.3.2: 970 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 971 | engines: {node: '>= 4'} 972 | 973 | ignore@7.0.5: 974 | resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} 975 | engines: {node: '>= 4'} 976 | 977 | import-fresh@3.3.1: 978 | resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 979 | engines: {node: '>=6'} 980 | 981 | imurmurhash@0.1.4: 982 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 983 | engines: {node: '>=0.8.19'} 984 | 985 | is-extglob@2.1.1: 986 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 987 | engines: {node: '>=0.10.0'} 988 | 989 | is-glob@4.0.3: 990 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 991 | engines: {node: '>=0.10.0'} 992 | 993 | is-number@7.0.0: 994 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 995 | engines: {node: '>=0.12.0'} 996 | 997 | isexe@2.0.0: 998 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 999 | 1000 | its-fine@2.0.0: 1001 | resolution: {integrity: sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==} 1002 | peerDependencies: 1003 | react: ^19.0.0 1004 | 1005 | jiti@2.5.1: 1006 | resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} 1007 | hasBin: true 1008 | 1009 | jotai@2.14.0: 1010 | resolution: {integrity: sha512-JQkNkTnqjk1BlSUjHfXi+pGG/573bVN104gp6CymhrWDseZGDReTNniWrLhJ+zXbM6pH+82+UNJ2vwYQUkQMWQ==} 1011 | engines: {node: '>=12.20.0'} 1012 | peerDependencies: 1013 | '@babel/core': '>=7.0.0' 1014 | '@babel/template': '>=7.0.0' 1015 | '@types/react': '>=17.0.0' 1016 | react: '>=17.0.0' 1017 | peerDependenciesMeta: 1018 | '@babel/core': 1019 | optional: true 1020 | '@babel/template': 1021 | optional: true 1022 | '@types/react': 1023 | optional: true 1024 | react: 1025 | optional: true 1026 | 1027 | js-base64@3.7.8: 1028 | resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} 1029 | 1030 | js-yaml@4.1.0: 1031 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1032 | hasBin: true 1033 | 1034 | json-buffer@3.0.1: 1035 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 1036 | 1037 | json-schema-traverse@0.4.1: 1038 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 1039 | 1040 | json-stable-stringify-without-jsonify@1.0.1: 1041 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 1042 | 1043 | keyv@4.5.4: 1044 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 1045 | 1046 | konva@9.3.22: 1047 | resolution: {integrity: sha512-yQI5d1bmELlD/fowuyfOp9ff+oamg26WOCkyqUyc+nczD/lhRa3EvD2MZOoc4c1293TAubW9n34fSQLgSeEgSw==} 1048 | 1049 | levn@0.4.1: 1050 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 1051 | engines: {node: '>= 0.8.0'} 1052 | 1053 | lightningcss-darwin-arm64@1.30.1: 1054 | resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} 1055 | engines: {node: '>= 12.0.0'} 1056 | cpu: [arm64] 1057 | os: [darwin] 1058 | 1059 | lightningcss-darwin-x64@1.30.1: 1060 | resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} 1061 | engines: {node: '>= 12.0.0'} 1062 | cpu: [x64] 1063 | os: [darwin] 1064 | 1065 | lightningcss-freebsd-x64@1.30.1: 1066 | resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} 1067 | engines: {node: '>= 12.0.0'} 1068 | cpu: [x64] 1069 | os: [freebsd] 1070 | 1071 | lightningcss-linux-arm-gnueabihf@1.30.1: 1072 | resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} 1073 | engines: {node: '>= 12.0.0'} 1074 | cpu: [arm] 1075 | os: [linux] 1076 | 1077 | lightningcss-linux-arm64-gnu@1.30.1: 1078 | resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} 1079 | engines: {node: '>= 12.0.0'} 1080 | cpu: [arm64] 1081 | os: [linux] 1082 | 1083 | lightningcss-linux-arm64-musl@1.30.1: 1084 | resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} 1085 | engines: {node: '>= 12.0.0'} 1086 | cpu: [arm64] 1087 | os: [linux] 1088 | 1089 | lightningcss-linux-x64-gnu@1.30.1: 1090 | resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} 1091 | engines: {node: '>= 12.0.0'} 1092 | cpu: [x64] 1093 | os: [linux] 1094 | 1095 | lightningcss-linux-x64-musl@1.30.1: 1096 | resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} 1097 | engines: {node: '>= 12.0.0'} 1098 | cpu: [x64] 1099 | os: [linux] 1100 | 1101 | lightningcss-win32-arm64-msvc@1.30.1: 1102 | resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} 1103 | engines: {node: '>= 12.0.0'} 1104 | cpu: [arm64] 1105 | os: [win32] 1106 | 1107 | lightningcss-win32-x64-msvc@1.30.1: 1108 | resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} 1109 | engines: {node: '>= 12.0.0'} 1110 | cpu: [x64] 1111 | os: [win32] 1112 | 1113 | lightningcss@1.30.1: 1114 | resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} 1115 | engines: {node: '>= 12.0.0'} 1116 | 1117 | locate-path@6.0.0: 1118 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 1119 | engines: {node: '>=10'} 1120 | 1121 | lodash.merge@4.6.2: 1122 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 1123 | 1124 | lucide-react@0.542.0: 1125 | resolution: {integrity: sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==} 1126 | peerDependencies: 1127 | react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 1128 | 1129 | magic-string@0.30.19: 1130 | resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} 1131 | 1132 | merge2@1.4.1: 1133 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1134 | engines: {node: '>= 8'} 1135 | 1136 | micromatch@4.0.8: 1137 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 1138 | engines: {node: '>=8.6'} 1139 | 1140 | minimatch@3.1.2: 1141 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 1142 | 1143 | minimatch@9.0.5: 1144 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 1145 | engines: {node: '>=16 || 14 >=14.17'} 1146 | 1147 | minipass@7.1.2: 1148 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 1149 | engines: {node: '>=16 || 14 >=14.17'} 1150 | 1151 | minizlib@3.0.2: 1152 | resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} 1153 | engines: {node: '>= 18'} 1154 | 1155 | mkdirp@3.0.1: 1156 | resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} 1157 | engines: {node: '>=10'} 1158 | hasBin: true 1159 | 1160 | ms@2.1.3: 1161 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1162 | 1163 | nanoid@3.3.11: 1164 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 1165 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1166 | hasBin: true 1167 | 1168 | natural-compare@1.4.0: 1169 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 1170 | 1171 | optionator@0.9.4: 1172 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 1173 | engines: {node: '>= 0.8.0'} 1174 | 1175 | p-limit@3.1.0: 1176 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 1177 | engines: {node: '>=10'} 1178 | 1179 | p-locate@5.0.0: 1180 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 1181 | engines: {node: '>=10'} 1182 | 1183 | parent-module@1.0.1: 1184 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 1185 | engines: {node: '>=6'} 1186 | 1187 | path-exists@4.0.0: 1188 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1189 | engines: {node: '>=8'} 1190 | 1191 | path-key@3.1.1: 1192 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1193 | engines: {node: '>=8'} 1194 | 1195 | picocolors@1.1.1: 1196 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 1197 | 1198 | picomatch@2.3.1: 1199 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1200 | engines: {node: '>=8.6'} 1201 | 1202 | picomatch@4.0.3: 1203 | resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 1204 | engines: {node: '>=12'} 1205 | 1206 | postcss@8.5.6: 1207 | resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 1208 | engines: {node: ^10 || ^12 || >=14} 1209 | 1210 | prelude-ls@1.2.1: 1211 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 1212 | engines: {node: '>= 0.8.0'} 1213 | 1214 | punycode@2.3.1: 1215 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 1216 | engines: {node: '>=6'} 1217 | 1218 | queue-microtask@1.2.3: 1219 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1220 | 1221 | react-dom@19.1.1: 1222 | resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} 1223 | peerDependencies: 1224 | react: ^19.1.1 1225 | 1226 | react-gridlines@1.1.7: 1227 | resolution: {integrity: sha512-7Hb2pJ9HXHaYD2cZHAroL2lUrOjQSappdN/aPSJqv7fYpiyq6ePUtf8qMnCxJdKlEEgo2jcGrMbaKCkdiTvEQw==} 1228 | peerDependencies: 1229 | react: '>= 16.10.0' 1230 | 1231 | react-konva@19.0.10: 1232 | resolution: {integrity: sha512-tJID7bungYs8ri5McjjlHWCniQMAasiNkTgATd8OJEnLrz3B7S5JN7nFkNfWETIHPx9rEN3gc+kQg32RhdZJcw==} 1233 | peerDependencies: 1234 | konva: ^8.0.1 || ^7.2.5 || ^9.0.0 || ^10.0.0 1235 | react: ^18.3.1 || ^19.0.0 1236 | react-dom: ^18.3.1 || ^19.0.0 1237 | 1238 | react-reconciler@0.32.0: 1239 | resolution: {integrity: sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==} 1240 | engines: {node: '>=0.10.0'} 1241 | peerDependencies: 1242 | react: ^19.1.0 1243 | 1244 | react@19.1.1: 1245 | resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} 1246 | engines: {node: '>=0.10.0'} 1247 | 1248 | resolve-from@4.0.0: 1249 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 1250 | engines: {node: '>=4'} 1251 | 1252 | reusify@1.1.0: 1253 | resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 1254 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1255 | 1256 | rollup@4.50.1: 1257 | resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} 1258 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1259 | hasBin: true 1260 | 1261 | run-parallel@1.2.0: 1262 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1263 | 1264 | scheduler@0.26.0: 1265 | resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} 1266 | 1267 | semver@7.7.2: 1268 | resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} 1269 | engines: {node: '>=10'} 1270 | hasBin: true 1271 | 1272 | shebang-command@2.0.0: 1273 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1274 | engines: {node: '>=8'} 1275 | 1276 | shebang-regex@3.0.0: 1277 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1278 | engines: {node: '>=8'} 1279 | 1280 | source-map-js@1.2.1: 1281 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1282 | engines: {node: '>=0.10.0'} 1283 | 1284 | strip-json-comments@3.1.1: 1285 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 1286 | engines: {node: '>=8'} 1287 | 1288 | supports-color@7.2.0: 1289 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1290 | engines: {node: '>=8'} 1291 | 1292 | tailwindcss@4.1.13: 1293 | resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} 1294 | 1295 | tapable@2.2.3: 1296 | resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} 1297 | engines: {node: '>=6'} 1298 | 1299 | tar@7.4.3: 1300 | resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} 1301 | engines: {node: '>=18'} 1302 | 1303 | tinyglobby@0.2.15: 1304 | resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} 1305 | engines: {node: '>=12.0.0'} 1306 | 1307 | to-regex-range@5.0.1: 1308 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1309 | engines: {node: '>=8.0'} 1310 | 1311 | ts-api-utils@2.1.0: 1312 | resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} 1313 | engines: {node: '>=18.12'} 1314 | peerDependencies: 1315 | typescript: '>=4.8.4' 1316 | 1317 | type-check@0.4.0: 1318 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 1319 | engines: {node: '>= 0.8.0'} 1320 | 1321 | typescript-eslint@8.43.0: 1322 | resolution: {integrity: sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w==} 1323 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1324 | peerDependencies: 1325 | eslint: ^8.57.0 || ^9.0.0 1326 | typescript: '>=4.8.4 <6.0.0' 1327 | 1328 | typescript@5.8.3: 1329 | resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 1330 | engines: {node: '>=14.17'} 1331 | hasBin: true 1332 | 1333 | uri-js@4.4.1: 1334 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1335 | 1336 | vite@7.1.5: 1337 | resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} 1338 | engines: {node: ^20.19.0 || >=22.12.0} 1339 | hasBin: true 1340 | peerDependencies: 1341 | '@types/node': ^20.19.0 || >=22.12.0 1342 | jiti: '>=1.21.0' 1343 | less: ^4.0.0 1344 | lightningcss: ^1.21.0 1345 | sass: ^1.70.0 1346 | sass-embedded: ^1.70.0 1347 | stylus: '>=0.54.8' 1348 | sugarss: ^5.0.0 1349 | terser: ^5.16.0 1350 | tsx: ^4.8.1 1351 | yaml: ^2.4.2 1352 | peerDependenciesMeta: 1353 | '@types/node': 1354 | optional: true 1355 | jiti: 1356 | optional: true 1357 | less: 1358 | optional: true 1359 | lightningcss: 1360 | optional: true 1361 | sass: 1362 | optional: true 1363 | sass-embedded: 1364 | optional: true 1365 | stylus: 1366 | optional: true 1367 | sugarss: 1368 | optional: true 1369 | terser: 1370 | optional: true 1371 | tsx: 1372 | optional: true 1373 | yaml: 1374 | optional: true 1375 | 1376 | which@2.0.2: 1377 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1378 | engines: {node: '>= 8'} 1379 | hasBin: true 1380 | 1381 | word-wrap@1.2.5: 1382 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 1383 | engines: {node: '>=0.10.0'} 1384 | 1385 | yallist@5.0.0: 1386 | resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} 1387 | engines: {node: '>=18'} 1388 | 1389 | yocto-queue@0.1.0: 1390 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1391 | engines: {node: '>=10'} 1392 | 1393 | snapshots: 1394 | 1395 | '@biomejs/biome@2.2.4': 1396 | optionalDependencies: 1397 | '@biomejs/cli-darwin-arm64': 2.2.4 1398 | '@biomejs/cli-darwin-x64': 2.2.4 1399 | '@biomejs/cli-linux-arm64': 2.2.4 1400 | '@biomejs/cli-linux-arm64-musl': 2.2.4 1401 | '@biomejs/cli-linux-x64': 2.2.4 1402 | '@biomejs/cli-linux-x64-musl': 2.2.4 1403 | '@biomejs/cli-win32-arm64': 2.2.4 1404 | '@biomejs/cli-win32-x64': 2.2.4 1405 | 1406 | '@biomejs/cli-darwin-arm64@2.2.4': 1407 | optional: true 1408 | 1409 | '@biomejs/cli-darwin-x64@2.2.4': 1410 | optional: true 1411 | 1412 | '@biomejs/cli-linux-arm64-musl@2.2.4': 1413 | optional: true 1414 | 1415 | '@biomejs/cli-linux-arm64@2.2.4': 1416 | optional: true 1417 | 1418 | '@biomejs/cli-linux-x64-musl@2.2.4': 1419 | optional: true 1420 | 1421 | '@biomejs/cli-linux-x64@2.2.4': 1422 | optional: true 1423 | 1424 | '@biomejs/cli-win32-arm64@2.2.4': 1425 | optional: true 1426 | 1427 | '@biomejs/cli-win32-x64@2.2.4': 1428 | optional: true 1429 | 1430 | '@esbuild/aix-ppc64@0.25.9': 1431 | optional: true 1432 | 1433 | '@esbuild/android-arm64@0.25.9': 1434 | optional: true 1435 | 1436 | '@esbuild/android-arm@0.25.9': 1437 | optional: true 1438 | 1439 | '@esbuild/android-x64@0.25.9': 1440 | optional: true 1441 | 1442 | '@esbuild/darwin-arm64@0.25.9': 1443 | optional: true 1444 | 1445 | '@esbuild/darwin-x64@0.25.9': 1446 | optional: true 1447 | 1448 | '@esbuild/freebsd-arm64@0.25.9': 1449 | optional: true 1450 | 1451 | '@esbuild/freebsd-x64@0.25.9': 1452 | optional: true 1453 | 1454 | '@esbuild/linux-arm64@0.25.9': 1455 | optional: true 1456 | 1457 | '@esbuild/linux-arm@0.25.9': 1458 | optional: true 1459 | 1460 | '@esbuild/linux-ia32@0.25.9': 1461 | optional: true 1462 | 1463 | '@esbuild/linux-loong64@0.25.9': 1464 | optional: true 1465 | 1466 | '@esbuild/linux-mips64el@0.25.9': 1467 | optional: true 1468 | 1469 | '@esbuild/linux-ppc64@0.25.9': 1470 | optional: true 1471 | 1472 | '@esbuild/linux-riscv64@0.25.9': 1473 | optional: true 1474 | 1475 | '@esbuild/linux-s390x@0.25.9': 1476 | optional: true 1477 | 1478 | '@esbuild/linux-x64@0.25.9': 1479 | optional: true 1480 | 1481 | '@esbuild/netbsd-arm64@0.25.9': 1482 | optional: true 1483 | 1484 | '@esbuild/netbsd-x64@0.25.9': 1485 | optional: true 1486 | 1487 | '@esbuild/openbsd-arm64@0.25.9': 1488 | optional: true 1489 | 1490 | '@esbuild/openbsd-x64@0.25.9': 1491 | optional: true 1492 | 1493 | '@esbuild/openharmony-arm64@0.25.9': 1494 | optional: true 1495 | 1496 | '@esbuild/sunos-x64@0.25.9': 1497 | optional: true 1498 | 1499 | '@esbuild/win32-arm64@0.25.9': 1500 | optional: true 1501 | 1502 | '@esbuild/win32-ia32@0.25.9': 1503 | optional: true 1504 | 1505 | '@esbuild/win32-x64@0.25.9': 1506 | optional: true 1507 | 1508 | '@eslint-community/eslint-utils@4.9.0(eslint@9.35.0(jiti@2.5.1))': 1509 | dependencies: 1510 | eslint: 9.35.0(jiti@2.5.1) 1511 | eslint-visitor-keys: 3.4.3 1512 | 1513 | '@eslint-community/regexpp@4.12.1': {} 1514 | 1515 | '@eslint/config-array@0.21.0': 1516 | dependencies: 1517 | '@eslint/object-schema': 2.1.6 1518 | debug: 4.4.1 1519 | minimatch: 3.1.2 1520 | transitivePeerDependencies: 1521 | - supports-color 1522 | 1523 | '@eslint/config-helpers@0.3.1': {} 1524 | 1525 | '@eslint/core@0.15.2': 1526 | dependencies: 1527 | '@types/json-schema': 7.0.15 1528 | 1529 | '@eslint/eslintrc@3.3.1': 1530 | dependencies: 1531 | ajv: 6.12.6 1532 | debug: 4.4.1 1533 | espree: 10.4.0 1534 | globals: 14.0.0 1535 | ignore: 5.3.2 1536 | import-fresh: 3.3.1 1537 | js-yaml: 4.1.0 1538 | minimatch: 3.1.2 1539 | strip-json-comments: 3.1.1 1540 | transitivePeerDependencies: 1541 | - supports-color 1542 | 1543 | '@eslint/js@9.35.0': {} 1544 | 1545 | '@eslint/object-schema@2.1.6': {} 1546 | 1547 | '@eslint/plugin-kit@0.3.5': 1548 | dependencies: 1549 | '@eslint/core': 0.15.2 1550 | levn: 0.4.1 1551 | 1552 | '@humanfs/core@0.19.1': {} 1553 | 1554 | '@humanfs/node@0.16.7': 1555 | dependencies: 1556 | '@humanfs/core': 0.19.1 1557 | '@humanwhocodes/retry': 0.4.3 1558 | 1559 | '@humanwhocodes/module-importer@1.0.1': {} 1560 | 1561 | '@humanwhocodes/retry@0.4.3': {} 1562 | 1563 | '@isaacs/fs-minipass@4.0.1': 1564 | dependencies: 1565 | minipass: 7.1.2 1566 | 1567 | '@jridgewell/gen-mapping@0.3.13': 1568 | dependencies: 1569 | '@jridgewell/sourcemap-codec': 1.5.5 1570 | '@jridgewell/trace-mapping': 0.3.31 1571 | 1572 | '@jridgewell/remapping@2.3.5': 1573 | dependencies: 1574 | '@jridgewell/gen-mapping': 0.3.13 1575 | '@jridgewell/trace-mapping': 0.3.31 1576 | 1577 | '@jridgewell/resolve-uri@3.1.2': {} 1578 | 1579 | '@jridgewell/sourcemap-codec@1.5.5': {} 1580 | 1581 | '@jridgewell/trace-mapping@0.3.31': 1582 | dependencies: 1583 | '@jridgewell/resolve-uri': 3.1.2 1584 | '@jridgewell/sourcemap-codec': 1.5.5 1585 | 1586 | '@nodelib/fs.scandir@2.1.5': 1587 | dependencies: 1588 | '@nodelib/fs.stat': 2.0.5 1589 | run-parallel: 1.2.0 1590 | 1591 | '@nodelib/fs.stat@2.0.5': {} 1592 | 1593 | '@nodelib/fs.walk@1.2.8': 1594 | dependencies: 1595 | '@nodelib/fs.scandir': 2.1.5 1596 | fastq: 1.19.1 1597 | 1598 | '@rolldown/pluginutils@1.0.0-beta.32': {} 1599 | 1600 | '@rollup/rollup-android-arm-eabi@4.50.1': 1601 | optional: true 1602 | 1603 | '@rollup/rollup-android-arm64@4.50.1': 1604 | optional: true 1605 | 1606 | '@rollup/rollup-darwin-arm64@4.50.1': 1607 | optional: true 1608 | 1609 | '@rollup/rollup-darwin-x64@4.50.1': 1610 | optional: true 1611 | 1612 | '@rollup/rollup-freebsd-arm64@4.50.1': 1613 | optional: true 1614 | 1615 | '@rollup/rollup-freebsd-x64@4.50.1': 1616 | optional: true 1617 | 1618 | '@rollup/rollup-linux-arm-gnueabihf@4.50.1': 1619 | optional: true 1620 | 1621 | '@rollup/rollup-linux-arm-musleabihf@4.50.1': 1622 | optional: true 1623 | 1624 | '@rollup/rollup-linux-arm64-gnu@4.50.1': 1625 | optional: true 1626 | 1627 | '@rollup/rollup-linux-arm64-musl@4.50.1': 1628 | optional: true 1629 | 1630 | '@rollup/rollup-linux-loongarch64-gnu@4.50.1': 1631 | optional: true 1632 | 1633 | '@rollup/rollup-linux-ppc64-gnu@4.50.1': 1634 | optional: true 1635 | 1636 | '@rollup/rollup-linux-riscv64-gnu@4.50.1': 1637 | optional: true 1638 | 1639 | '@rollup/rollup-linux-riscv64-musl@4.50.1': 1640 | optional: true 1641 | 1642 | '@rollup/rollup-linux-s390x-gnu@4.50.1': 1643 | optional: true 1644 | 1645 | '@rollup/rollup-linux-x64-gnu@4.50.1': 1646 | optional: true 1647 | 1648 | '@rollup/rollup-linux-x64-musl@4.50.1': 1649 | optional: true 1650 | 1651 | '@rollup/rollup-openharmony-arm64@4.50.1': 1652 | optional: true 1653 | 1654 | '@rollup/rollup-win32-arm64-msvc@4.50.1': 1655 | optional: true 1656 | 1657 | '@rollup/rollup-win32-ia32-msvc@4.50.1': 1658 | optional: true 1659 | 1660 | '@rollup/rollup-win32-x64-msvc@4.50.1': 1661 | optional: true 1662 | 1663 | '@swc/core-darwin-arm64@1.13.5': 1664 | optional: true 1665 | 1666 | '@swc/core-darwin-x64@1.13.5': 1667 | optional: true 1668 | 1669 | '@swc/core-linux-arm-gnueabihf@1.13.5': 1670 | optional: true 1671 | 1672 | '@swc/core-linux-arm64-gnu@1.13.5': 1673 | optional: true 1674 | 1675 | '@swc/core-linux-arm64-musl@1.13.5': 1676 | optional: true 1677 | 1678 | '@swc/core-linux-x64-gnu@1.13.5': 1679 | optional: true 1680 | 1681 | '@swc/core-linux-x64-musl@1.13.5': 1682 | optional: true 1683 | 1684 | '@swc/core-win32-arm64-msvc@1.13.5': 1685 | optional: true 1686 | 1687 | '@swc/core-win32-ia32-msvc@1.13.5': 1688 | optional: true 1689 | 1690 | '@swc/core-win32-x64-msvc@1.13.5': 1691 | optional: true 1692 | 1693 | '@swc/core@1.13.5': 1694 | dependencies: 1695 | '@swc/counter': 0.1.3 1696 | '@swc/types': 0.1.25 1697 | optionalDependencies: 1698 | '@swc/core-darwin-arm64': 1.13.5 1699 | '@swc/core-darwin-x64': 1.13.5 1700 | '@swc/core-linux-arm-gnueabihf': 1.13.5 1701 | '@swc/core-linux-arm64-gnu': 1.13.5 1702 | '@swc/core-linux-arm64-musl': 1.13.5 1703 | '@swc/core-linux-x64-gnu': 1.13.5 1704 | '@swc/core-linux-x64-musl': 1.13.5 1705 | '@swc/core-win32-arm64-msvc': 1.13.5 1706 | '@swc/core-win32-ia32-msvc': 1.13.5 1707 | '@swc/core-win32-x64-msvc': 1.13.5 1708 | 1709 | '@swc/counter@0.1.3': {} 1710 | 1711 | '@swc/types@0.1.25': 1712 | dependencies: 1713 | '@swc/counter': 0.1.3 1714 | 1715 | '@tailwindcss/node@4.1.13': 1716 | dependencies: 1717 | '@jridgewell/remapping': 2.3.5 1718 | enhanced-resolve: 5.18.3 1719 | jiti: 2.5.1 1720 | lightningcss: 1.30.1 1721 | magic-string: 0.30.19 1722 | source-map-js: 1.2.1 1723 | tailwindcss: 4.1.13 1724 | 1725 | '@tailwindcss/oxide-android-arm64@4.1.13': 1726 | optional: true 1727 | 1728 | '@tailwindcss/oxide-darwin-arm64@4.1.13': 1729 | optional: true 1730 | 1731 | '@tailwindcss/oxide-darwin-x64@4.1.13': 1732 | optional: true 1733 | 1734 | '@tailwindcss/oxide-freebsd-x64@4.1.13': 1735 | optional: true 1736 | 1737 | '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': 1738 | optional: true 1739 | 1740 | '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': 1741 | optional: true 1742 | 1743 | '@tailwindcss/oxide-linux-arm64-musl@4.1.13': 1744 | optional: true 1745 | 1746 | '@tailwindcss/oxide-linux-x64-gnu@4.1.13': 1747 | optional: true 1748 | 1749 | '@tailwindcss/oxide-linux-x64-musl@4.1.13': 1750 | optional: true 1751 | 1752 | '@tailwindcss/oxide-wasm32-wasi@4.1.13': 1753 | optional: true 1754 | 1755 | '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': 1756 | optional: true 1757 | 1758 | '@tailwindcss/oxide-win32-x64-msvc@4.1.13': 1759 | optional: true 1760 | 1761 | '@tailwindcss/oxide@4.1.13': 1762 | dependencies: 1763 | detect-libc: 2.0.4 1764 | tar: 7.4.3 1765 | optionalDependencies: 1766 | '@tailwindcss/oxide-android-arm64': 4.1.13 1767 | '@tailwindcss/oxide-darwin-arm64': 4.1.13 1768 | '@tailwindcss/oxide-darwin-x64': 4.1.13 1769 | '@tailwindcss/oxide-freebsd-x64': 4.1.13 1770 | '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 1771 | '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 1772 | '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 1773 | '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 1774 | '@tailwindcss/oxide-linux-x64-musl': 4.1.13 1775 | '@tailwindcss/oxide-wasm32-wasi': 4.1.13 1776 | '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 1777 | '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 1778 | 1779 | '@tailwindcss/vite@4.1.13(vite@7.1.5(jiti@2.5.1)(lightningcss@1.30.1))': 1780 | dependencies: 1781 | '@tailwindcss/node': 4.1.13 1782 | '@tailwindcss/oxide': 4.1.13 1783 | tailwindcss: 4.1.13 1784 | vite: 7.1.5(jiti@2.5.1)(lightningcss@1.30.1) 1785 | 1786 | '@types/estree@1.0.8': {} 1787 | 1788 | '@types/json-schema@7.0.15': {} 1789 | 1790 | '@types/react-dom@19.1.9(@types/react@19.1.12)': 1791 | dependencies: 1792 | '@types/react': 19.1.12 1793 | 1794 | '@types/react-reconciler@0.28.9(@types/react@19.1.12)': 1795 | dependencies: 1796 | '@types/react': 19.1.12 1797 | 1798 | '@types/react-reconciler@0.32.1(@types/react@19.1.12)': 1799 | dependencies: 1800 | '@types/react': 19.1.12 1801 | 1802 | '@types/react@19.1.12': 1803 | dependencies: 1804 | csstype: 3.1.3 1805 | 1806 | '@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3)': 1807 | dependencies: 1808 | '@eslint-community/regexpp': 4.12.1 1809 | '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3) 1810 | '@typescript-eslint/scope-manager': 8.43.0 1811 | '@typescript-eslint/type-utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3) 1812 | '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3) 1813 | '@typescript-eslint/visitor-keys': 8.43.0 1814 | eslint: 9.35.0(jiti@2.5.1) 1815 | graphemer: 1.4.0 1816 | ignore: 7.0.5 1817 | natural-compare: 1.4.0 1818 | ts-api-utils: 2.1.0(typescript@5.8.3) 1819 | typescript: 5.8.3 1820 | transitivePeerDependencies: 1821 | - supports-color 1822 | 1823 | '@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3)': 1824 | dependencies: 1825 | '@typescript-eslint/scope-manager': 8.43.0 1826 | '@typescript-eslint/types': 8.43.0 1827 | '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.8.3) 1828 | '@typescript-eslint/visitor-keys': 8.43.0 1829 | debug: 4.4.1 1830 | eslint: 9.35.0(jiti@2.5.1) 1831 | typescript: 5.8.3 1832 | transitivePeerDependencies: 1833 | - supports-color 1834 | 1835 | '@typescript-eslint/project-service@8.43.0(typescript@5.8.3)': 1836 | dependencies: 1837 | '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.8.3) 1838 | '@typescript-eslint/types': 8.43.0 1839 | debug: 4.4.1 1840 | typescript: 5.8.3 1841 | transitivePeerDependencies: 1842 | - supports-color 1843 | 1844 | '@typescript-eslint/scope-manager@8.43.0': 1845 | dependencies: 1846 | '@typescript-eslint/types': 8.43.0 1847 | '@typescript-eslint/visitor-keys': 8.43.0 1848 | 1849 | '@typescript-eslint/tsconfig-utils@8.43.0(typescript@5.8.3)': 1850 | dependencies: 1851 | typescript: 5.8.3 1852 | 1853 | '@typescript-eslint/type-utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3)': 1854 | dependencies: 1855 | '@typescript-eslint/types': 8.43.0 1856 | '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.8.3) 1857 | '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3) 1858 | debug: 4.4.1 1859 | eslint: 9.35.0(jiti@2.5.1) 1860 | ts-api-utils: 2.1.0(typescript@5.8.3) 1861 | typescript: 5.8.3 1862 | transitivePeerDependencies: 1863 | - supports-color 1864 | 1865 | '@typescript-eslint/types@8.43.0': {} 1866 | 1867 | '@typescript-eslint/typescript-estree@8.43.0(typescript@5.8.3)': 1868 | dependencies: 1869 | '@typescript-eslint/project-service': 8.43.0(typescript@5.8.3) 1870 | '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.8.3) 1871 | '@typescript-eslint/types': 8.43.0 1872 | '@typescript-eslint/visitor-keys': 8.43.0 1873 | debug: 4.4.1 1874 | fast-glob: 3.3.3 1875 | is-glob: 4.0.3 1876 | minimatch: 9.0.5 1877 | semver: 7.7.2 1878 | ts-api-utils: 2.1.0(typescript@5.8.3) 1879 | typescript: 5.8.3 1880 | transitivePeerDependencies: 1881 | - supports-color 1882 | 1883 | '@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3)': 1884 | dependencies: 1885 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) 1886 | '@typescript-eslint/scope-manager': 8.43.0 1887 | '@typescript-eslint/types': 8.43.0 1888 | '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.8.3) 1889 | eslint: 9.35.0(jiti@2.5.1) 1890 | typescript: 5.8.3 1891 | transitivePeerDependencies: 1892 | - supports-color 1893 | 1894 | '@typescript-eslint/visitor-keys@8.43.0': 1895 | dependencies: 1896 | '@typescript-eslint/types': 8.43.0 1897 | eslint-visitor-keys: 4.2.1 1898 | 1899 | '@vitejs/plugin-react-swc@4.0.1(vite@7.1.5(jiti@2.5.1)(lightningcss@1.30.1))': 1900 | dependencies: 1901 | '@rolldown/pluginutils': 1.0.0-beta.32 1902 | '@swc/core': 1.13.5 1903 | vite: 7.1.5(jiti@2.5.1)(lightningcss@1.30.1) 1904 | transitivePeerDependencies: 1905 | - '@swc/helpers' 1906 | 1907 | acorn-jsx@5.3.2(acorn@8.15.0): 1908 | dependencies: 1909 | acorn: 8.15.0 1910 | 1911 | acorn@8.15.0: {} 1912 | 1913 | ajv@6.12.6: 1914 | dependencies: 1915 | fast-deep-equal: 3.1.3 1916 | fast-json-stable-stringify: 2.1.0 1917 | json-schema-traverse: 0.4.1 1918 | uri-js: 4.4.1 1919 | 1920 | ansi-styles@4.3.0: 1921 | dependencies: 1922 | color-convert: 2.0.1 1923 | 1924 | argparse@2.0.1: {} 1925 | 1926 | balanced-match@1.0.2: {} 1927 | 1928 | brace-expansion@1.1.12: 1929 | dependencies: 1930 | balanced-match: 1.0.2 1931 | concat-map: 0.0.1 1932 | 1933 | brace-expansion@2.0.2: 1934 | dependencies: 1935 | balanced-match: 1.0.2 1936 | 1937 | braces@3.0.3: 1938 | dependencies: 1939 | fill-range: 7.1.1 1940 | 1941 | callsites@3.1.0: {} 1942 | 1943 | chalk@4.1.2: 1944 | dependencies: 1945 | ansi-styles: 4.3.0 1946 | supports-color: 7.2.0 1947 | 1948 | chownr@3.0.0: {} 1949 | 1950 | clsx@2.1.1: {} 1951 | 1952 | color-convert@2.0.1: 1953 | dependencies: 1954 | color-name: 1.1.4 1955 | 1956 | color-name@1.1.4: {} 1957 | 1958 | concat-map@0.0.1: {} 1959 | 1960 | cross-spawn@7.0.6: 1961 | dependencies: 1962 | path-key: 3.1.1 1963 | shebang-command: 2.0.0 1964 | which: 2.0.2 1965 | 1966 | csstype@3.1.3: {} 1967 | 1968 | debug@4.4.1: 1969 | dependencies: 1970 | ms: 2.1.3 1971 | 1972 | deep-is@0.1.4: {} 1973 | 1974 | detect-libc@2.0.4: {} 1975 | 1976 | enhanced-resolve@5.18.3: 1977 | dependencies: 1978 | graceful-fs: 4.2.11 1979 | tapable: 2.2.3 1980 | 1981 | esbuild@0.25.9: 1982 | optionalDependencies: 1983 | '@esbuild/aix-ppc64': 0.25.9 1984 | '@esbuild/android-arm': 0.25.9 1985 | '@esbuild/android-arm64': 0.25.9 1986 | '@esbuild/android-x64': 0.25.9 1987 | '@esbuild/darwin-arm64': 0.25.9 1988 | '@esbuild/darwin-x64': 0.25.9 1989 | '@esbuild/freebsd-arm64': 0.25.9 1990 | '@esbuild/freebsd-x64': 0.25.9 1991 | '@esbuild/linux-arm': 0.25.9 1992 | '@esbuild/linux-arm64': 0.25.9 1993 | '@esbuild/linux-ia32': 0.25.9 1994 | '@esbuild/linux-loong64': 0.25.9 1995 | '@esbuild/linux-mips64el': 0.25.9 1996 | '@esbuild/linux-ppc64': 0.25.9 1997 | '@esbuild/linux-riscv64': 0.25.9 1998 | '@esbuild/linux-s390x': 0.25.9 1999 | '@esbuild/linux-x64': 0.25.9 2000 | '@esbuild/netbsd-arm64': 0.25.9 2001 | '@esbuild/netbsd-x64': 0.25.9 2002 | '@esbuild/openbsd-arm64': 0.25.9 2003 | '@esbuild/openbsd-x64': 0.25.9 2004 | '@esbuild/openharmony-arm64': 0.25.9 2005 | '@esbuild/sunos-x64': 0.25.9 2006 | '@esbuild/win32-arm64': 0.25.9 2007 | '@esbuild/win32-ia32': 0.25.9 2008 | '@esbuild/win32-x64': 0.25.9 2009 | 2010 | escape-string-regexp@4.0.0: {} 2011 | 2012 | eslint-plugin-react-hooks@5.2.0(eslint@9.35.0(jiti@2.5.1)): 2013 | dependencies: 2014 | eslint: 9.35.0(jiti@2.5.1) 2015 | 2016 | eslint-plugin-react-refresh@0.4.20(eslint@9.35.0(jiti@2.5.1)): 2017 | dependencies: 2018 | eslint: 9.35.0(jiti@2.5.1) 2019 | 2020 | eslint-scope@8.4.0: 2021 | dependencies: 2022 | esrecurse: 4.3.0 2023 | estraverse: 5.3.0 2024 | 2025 | eslint-visitor-keys@3.4.3: {} 2026 | 2027 | eslint-visitor-keys@4.2.1: {} 2028 | 2029 | eslint@9.35.0(jiti@2.5.1): 2030 | dependencies: 2031 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) 2032 | '@eslint-community/regexpp': 4.12.1 2033 | '@eslint/config-array': 0.21.0 2034 | '@eslint/config-helpers': 0.3.1 2035 | '@eslint/core': 0.15.2 2036 | '@eslint/eslintrc': 3.3.1 2037 | '@eslint/js': 9.35.0 2038 | '@eslint/plugin-kit': 0.3.5 2039 | '@humanfs/node': 0.16.7 2040 | '@humanwhocodes/module-importer': 1.0.1 2041 | '@humanwhocodes/retry': 0.4.3 2042 | '@types/estree': 1.0.8 2043 | '@types/json-schema': 7.0.15 2044 | ajv: 6.12.6 2045 | chalk: 4.1.2 2046 | cross-spawn: 7.0.6 2047 | debug: 4.4.1 2048 | escape-string-regexp: 4.0.0 2049 | eslint-scope: 8.4.0 2050 | eslint-visitor-keys: 4.2.1 2051 | espree: 10.4.0 2052 | esquery: 1.6.0 2053 | esutils: 2.0.3 2054 | fast-deep-equal: 3.1.3 2055 | file-entry-cache: 8.0.0 2056 | find-up: 5.0.0 2057 | glob-parent: 6.0.2 2058 | ignore: 5.3.2 2059 | imurmurhash: 0.1.4 2060 | is-glob: 4.0.3 2061 | json-stable-stringify-without-jsonify: 1.0.1 2062 | lodash.merge: 4.6.2 2063 | minimatch: 3.1.2 2064 | natural-compare: 1.4.0 2065 | optionator: 0.9.4 2066 | optionalDependencies: 2067 | jiti: 2.5.1 2068 | transitivePeerDependencies: 2069 | - supports-color 2070 | 2071 | espree@10.4.0: 2072 | dependencies: 2073 | acorn: 8.15.0 2074 | acorn-jsx: 5.3.2(acorn@8.15.0) 2075 | eslint-visitor-keys: 4.2.1 2076 | 2077 | esquery@1.6.0: 2078 | dependencies: 2079 | estraverse: 5.3.0 2080 | 2081 | esrecurse@4.3.0: 2082 | dependencies: 2083 | estraverse: 5.3.0 2084 | 2085 | estraverse@5.3.0: {} 2086 | 2087 | esutils@2.0.3: {} 2088 | 2089 | fast-deep-equal@3.1.3: {} 2090 | 2091 | fast-glob@3.3.3: 2092 | dependencies: 2093 | '@nodelib/fs.stat': 2.0.5 2094 | '@nodelib/fs.walk': 1.2.8 2095 | glob-parent: 5.1.2 2096 | merge2: 1.4.1 2097 | micromatch: 4.0.8 2098 | 2099 | fast-json-stable-stringify@2.1.0: {} 2100 | 2101 | fast-levenshtein@2.0.6: {} 2102 | 2103 | fastq@1.19.1: 2104 | dependencies: 2105 | reusify: 1.1.0 2106 | 2107 | fdir@6.5.0(picomatch@4.0.3): 2108 | optionalDependencies: 2109 | picomatch: 4.0.3 2110 | 2111 | file-entry-cache@8.0.0: 2112 | dependencies: 2113 | flat-cache: 4.0.1 2114 | 2115 | fill-range@7.1.1: 2116 | dependencies: 2117 | to-regex-range: 5.0.1 2118 | 2119 | find-up@5.0.0: 2120 | dependencies: 2121 | locate-path: 6.0.0 2122 | path-exists: 4.0.0 2123 | 2124 | flat-cache@4.0.1: 2125 | dependencies: 2126 | flatted: 3.3.3 2127 | keyv: 4.5.4 2128 | 2129 | flatted@3.3.3: {} 2130 | 2131 | fsevents@2.3.3: 2132 | optional: true 2133 | 2134 | glob-parent@5.1.2: 2135 | dependencies: 2136 | is-glob: 4.0.3 2137 | 2138 | glob-parent@6.0.2: 2139 | dependencies: 2140 | is-glob: 4.0.3 2141 | 2142 | globals@14.0.0: {} 2143 | 2144 | globals@16.4.0: {} 2145 | 2146 | graceful-fs@4.2.11: {} 2147 | 2148 | graphemer@1.4.0: {} 2149 | 2150 | has-flag@4.0.0: {} 2151 | 2152 | husky@9.1.7: {} 2153 | 2154 | ignore@5.3.2: {} 2155 | 2156 | ignore@7.0.5: {} 2157 | 2158 | import-fresh@3.3.1: 2159 | dependencies: 2160 | parent-module: 1.0.1 2161 | resolve-from: 4.0.0 2162 | 2163 | imurmurhash@0.1.4: {} 2164 | 2165 | is-extglob@2.1.1: {} 2166 | 2167 | is-glob@4.0.3: 2168 | dependencies: 2169 | is-extglob: 2.1.1 2170 | 2171 | is-number@7.0.0: {} 2172 | 2173 | isexe@2.0.0: {} 2174 | 2175 | its-fine@2.0.0(@types/react@19.1.12)(react@19.1.1): 2176 | dependencies: 2177 | '@types/react-reconciler': 0.28.9(@types/react@19.1.12) 2178 | react: 19.1.1 2179 | transitivePeerDependencies: 2180 | - '@types/react' 2181 | 2182 | jiti@2.5.1: {} 2183 | 2184 | jotai@2.14.0(@types/react@19.1.12)(react@19.1.1): 2185 | optionalDependencies: 2186 | '@types/react': 19.1.12 2187 | react: 19.1.1 2188 | 2189 | js-base64@3.7.8: {} 2190 | 2191 | js-yaml@4.1.0: 2192 | dependencies: 2193 | argparse: 2.0.1 2194 | 2195 | json-buffer@3.0.1: {} 2196 | 2197 | json-schema-traverse@0.4.1: {} 2198 | 2199 | json-stable-stringify-without-jsonify@1.0.1: {} 2200 | 2201 | keyv@4.5.4: 2202 | dependencies: 2203 | json-buffer: 3.0.1 2204 | 2205 | konva@9.3.22: {} 2206 | 2207 | levn@0.4.1: 2208 | dependencies: 2209 | prelude-ls: 1.2.1 2210 | type-check: 0.4.0 2211 | 2212 | lightningcss-darwin-arm64@1.30.1: 2213 | optional: true 2214 | 2215 | lightningcss-darwin-x64@1.30.1: 2216 | optional: true 2217 | 2218 | lightningcss-freebsd-x64@1.30.1: 2219 | optional: true 2220 | 2221 | lightningcss-linux-arm-gnueabihf@1.30.1: 2222 | optional: true 2223 | 2224 | lightningcss-linux-arm64-gnu@1.30.1: 2225 | optional: true 2226 | 2227 | lightningcss-linux-arm64-musl@1.30.1: 2228 | optional: true 2229 | 2230 | lightningcss-linux-x64-gnu@1.30.1: 2231 | optional: true 2232 | 2233 | lightningcss-linux-x64-musl@1.30.1: 2234 | optional: true 2235 | 2236 | lightningcss-win32-arm64-msvc@1.30.1: 2237 | optional: true 2238 | 2239 | lightningcss-win32-x64-msvc@1.30.1: 2240 | optional: true 2241 | 2242 | lightningcss@1.30.1: 2243 | dependencies: 2244 | detect-libc: 2.0.4 2245 | optionalDependencies: 2246 | lightningcss-darwin-arm64: 1.30.1 2247 | lightningcss-darwin-x64: 1.30.1 2248 | lightningcss-freebsd-x64: 1.30.1 2249 | lightningcss-linux-arm-gnueabihf: 1.30.1 2250 | lightningcss-linux-arm64-gnu: 1.30.1 2251 | lightningcss-linux-arm64-musl: 1.30.1 2252 | lightningcss-linux-x64-gnu: 1.30.1 2253 | lightningcss-linux-x64-musl: 1.30.1 2254 | lightningcss-win32-arm64-msvc: 1.30.1 2255 | lightningcss-win32-x64-msvc: 1.30.1 2256 | 2257 | locate-path@6.0.0: 2258 | dependencies: 2259 | p-locate: 5.0.0 2260 | 2261 | lodash.merge@4.6.2: {} 2262 | 2263 | lucide-react@0.542.0(react@19.1.1): 2264 | dependencies: 2265 | react: 19.1.1 2266 | 2267 | magic-string@0.30.19: 2268 | dependencies: 2269 | '@jridgewell/sourcemap-codec': 1.5.5 2270 | 2271 | merge2@1.4.1: {} 2272 | 2273 | micromatch@4.0.8: 2274 | dependencies: 2275 | braces: 3.0.3 2276 | picomatch: 2.3.1 2277 | 2278 | minimatch@3.1.2: 2279 | dependencies: 2280 | brace-expansion: 1.1.12 2281 | 2282 | minimatch@9.0.5: 2283 | dependencies: 2284 | brace-expansion: 2.0.2 2285 | 2286 | minipass@7.1.2: {} 2287 | 2288 | minizlib@3.0.2: 2289 | dependencies: 2290 | minipass: 7.1.2 2291 | 2292 | mkdirp@3.0.1: {} 2293 | 2294 | ms@2.1.3: {} 2295 | 2296 | nanoid@3.3.11: {} 2297 | 2298 | natural-compare@1.4.0: {} 2299 | 2300 | optionator@0.9.4: 2301 | dependencies: 2302 | deep-is: 0.1.4 2303 | fast-levenshtein: 2.0.6 2304 | levn: 0.4.1 2305 | prelude-ls: 1.2.1 2306 | type-check: 0.4.0 2307 | word-wrap: 1.2.5 2308 | 2309 | p-limit@3.1.0: 2310 | dependencies: 2311 | yocto-queue: 0.1.0 2312 | 2313 | p-locate@5.0.0: 2314 | dependencies: 2315 | p-limit: 3.1.0 2316 | 2317 | parent-module@1.0.1: 2318 | dependencies: 2319 | callsites: 3.1.0 2320 | 2321 | path-exists@4.0.0: {} 2322 | 2323 | path-key@3.1.1: {} 2324 | 2325 | picocolors@1.1.1: {} 2326 | 2327 | picomatch@2.3.1: {} 2328 | 2329 | picomatch@4.0.3: {} 2330 | 2331 | postcss@8.5.6: 2332 | dependencies: 2333 | nanoid: 3.3.11 2334 | picocolors: 1.1.1 2335 | source-map-js: 1.2.1 2336 | 2337 | prelude-ls@1.2.1: {} 2338 | 2339 | punycode@2.3.1: {} 2340 | 2341 | queue-microtask@1.2.3: {} 2342 | 2343 | react-dom@19.1.1(react@19.1.1): 2344 | dependencies: 2345 | react: 19.1.1 2346 | scheduler: 0.26.0 2347 | 2348 | react-gridlines@1.1.7(react@19.1.1): 2349 | dependencies: 2350 | js-base64: 3.7.8 2351 | react: 19.1.1 2352 | 2353 | react-konva@19.0.10(@types/react@19.1.12)(konva@9.3.22)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): 2354 | dependencies: 2355 | '@types/react-reconciler': 0.32.1(@types/react@19.1.12) 2356 | its-fine: 2.0.0(@types/react@19.1.12)(react@19.1.1) 2357 | konva: 9.3.22 2358 | react: 19.1.1 2359 | react-dom: 19.1.1(react@19.1.1) 2360 | react-reconciler: 0.32.0(react@19.1.1) 2361 | scheduler: 0.26.0 2362 | transitivePeerDependencies: 2363 | - '@types/react' 2364 | 2365 | react-reconciler@0.32.0(react@19.1.1): 2366 | dependencies: 2367 | react: 19.1.1 2368 | scheduler: 0.26.0 2369 | 2370 | react@19.1.1: {} 2371 | 2372 | resolve-from@4.0.0: {} 2373 | 2374 | reusify@1.1.0: {} 2375 | 2376 | rollup@4.50.1: 2377 | dependencies: 2378 | '@types/estree': 1.0.8 2379 | optionalDependencies: 2380 | '@rollup/rollup-android-arm-eabi': 4.50.1 2381 | '@rollup/rollup-android-arm64': 4.50.1 2382 | '@rollup/rollup-darwin-arm64': 4.50.1 2383 | '@rollup/rollup-darwin-x64': 4.50.1 2384 | '@rollup/rollup-freebsd-arm64': 4.50.1 2385 | '@rollup/rollup-freebsd-x64': 4.50.1 2386 | '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 2387 | '@rollup/rollup-linux-arm-musleabihf': 4.50.1 2388 | '@rollup/rollup-linux-arm64-gnu': 4.50.1 2389 | '@rollup/rollup-linux-arm64-musl': 4.50.1 2390 | '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 2391 | '@rollup/rollup-linux-ppc64-gnu': 4.50.1 2392 | '@rollup/rollup-linux-riscv64-gnu': 4.50.1 2393 | '@rollup/rollup-linux-riscv64-musl': 4.50.1 2394 | '@rollup/rollup-linux-s390x-gnu': 4.50.1 2395 | '@rollup/rollup-linux-x64-gnu': 4.50.1 2396 | '@rollup/rollup-linux-x64-musl': 4.50.1 2397 | '@rollup/rollup-openharmony-arm64': 4.50.1 2398 | '@rollup/rollup-win32-arm64-msvc': 4.50.1 2399 | '@rollup/rollup-win32-ia32-msvc': 4.50.1 2400 | '@rollup/rollup-win32-x64-msvc': 4.50.1 2401 | fsevents: 2.3.3 2402 | 2403 | run-parallel@1.2.0: 2404 | dependencies: 2405 | queue-microtask: 1.2.3 2406 | 2407 | scheduler@0.26.0: {} 2408 | 2409 | semver@7.7.2: {} 2410 | 2411 | shebang-command@2.0.0: 2412 | dependencies: 2413 | shebang-regex: 3.0.0 2414 | 2415 | shebang-regex@3.0.0: {} 2416 | 2417 | source-map-js@1.2.1: {} 2418 | 2419 | strip-json-comments@3.1.1: {} 2420 | 2421 | supports-color@7.2.0: 2422 | dependencies: 2423 | has-flag: 4.0.0 2424 | 2425 | tailwindcss@4.1.13: {} 2426 | 2427 | tapable@2.2.3: {} 2428 | 2429 | tar@7.4.3: 2430 | dependencies: 2431 | '@isaacs/fs-minipass': 4.0.1 2432 | chownr: 3.0.0 2433 | minipass: 7.1.2 2434 | minizlib: 3.0.2 2435 | mkdirp: 3.0.1 2436 | yallist: 5.0.0 2437 | 2438 | tinyglobby@0.2.15: 2439 | dependencies: 2440 | fdir: 6.5.0(picomatch@4.0.3) 2441 | picomatch: 4.0.3 2442 | 2443 | to-regex-range@5.0.1: 2444 | dependencies: 2445 | is-number: 7.0.0 2446 | 2447 | ts-api-utils@2.1.0(typescript@5.8.3): 2448 | dependencies: 2449 | typescript: 5.8.3 2450 | 2451 | type-check@0.4.0: 2452 | dependencies: 2453 | prelude-ls: 1.2.1 2454 | 2455 | typescript-eslint@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3): 2456 | dependencies: 2457 | '@typescript-eslint/eslint-plugin': 8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3) 2458 | '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3) 2459 | '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.8.3) 2460 | '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.8.3) 2461 | eslint: 9.35.0(jiti@2.5.1) 2462 | typescript: 5.8.3 2463 | transitivePeerDependencies: 2464 | - supports-color 2465 | 2466 | typescript@5.8.3: {} 2467 | 2468 | uri-js@4.4.1: 2469 | dependencies: 2470 | punycode: 2.3.1 2471 | 2472 | vite@7.1.5(jiti@2.5.1)(lightningcss@1.30.1): 2473 | dependencies: 2474 | esbuild: 0.25.9 2475 | fdir: 6.5.0(picomatch@4.0.3) 2476 | picomatch: 4.0.3 2477 | postcss: 8.5.6 2478 | rollup: 4.50.1 2479 | tinyglobby: 0.2.15 2480 | optionalDependencies: 2481 | fsevents: 2.3.3 2482 | jiti: 2.5.1 2483 | lightningcss: 1.30.1 2484 | 2485 | which@2.0.2: 2486 | dependencies: 2487 | isexe: 2.0.0 2488 | 2489 | word-wrap@1.2.5: {} 2490 | 2491 | yallist@5.0.0: {} 2492 | 2493 | yocto-queue@0.1.0: {} 2494 | -------------------------------------------------------------------------------- /public/fsm-engine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import Alert from "./components/Alert"; 2 | import Dock from "./components/Dock"; 3 | import Editor from "./components/Editor"; 4 | import Settings from "./components/Settings"; 5 | import Error from "./components/Error"; 6 | import Welcome from "./components/Welcome"; 7 | import { alert } from "./lib/backend"; 8 | import { useAtomValue } from "jotai"; 9 | 10 | // @ts-ignore 11 | import GridLines from "react-gridlines"; 12 | 13 | function App() { 14 | const alertMsg = useAtomValue(alert); 15 | 16 | return ( 17 | <> 18 |
22 | 30 | 31 | 32 |
33 | 34 | 35 | 36 | {/* Settings Menu */} 37 | 38 | 39 | {/* Alert Box */} 40 | 41 | 42 | {/* Error Page */} 43 | 44 | 45 | {/* Welcome Page */} 46 | 47 | 48 | ); 49 | } 50 | 51 | export default App; 52 | -------------------------------------------------------------------------------- /src/assets/github_font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthik-saiharsh/fsm-engine/4da4ed9912d26edce2a68884739e68413c060923/src/assets/github_font.ttf -------------------------------------------------------------------------------- /src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { alert } from "../lib/backend"; 2 | import { useAtomValue } from "jotai"; 3 | import clsx from "clsx"; 4 | 5 | const Alert = (props: { message: string }) => { 6 | const alertMsg = useAtomValue(alert); 7 | 8 | return ( 9 |
18 | 19 |

20 | {props.message} 21 |

22 |
23 |
24 | ); 25 | }; 26 | 27 | export default Alert; 28 | -------------------------------------------------------------------------------- /src/components/Dock.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Move3d, 3 | PlusCircleIcon, 4 | MinusCircleIcon, 5 | Settings, 6 | Cable, 7 | HardDriveDownload, 8 | BookMarked, 9 | } from "lucide-react"; 10 | import clsx from "clsx"; 11 | import { 12 | editorState, 13 | currentSelected, 14 | alert, 15 | arrowStates, 16 | saveFSMAtom, 17 | } from "../lib/backend"; 18 | import { useAtomValue, useSetAtom } from "jotai"; 19 | 20 | const Dock = () => { 21 | const DockIconSize = 24; 22 | const DockIconColor = "#ffffff"; 23 | 24 | // Global editor state store 25 | const currentState = useAtomValue(editorState); 26 | const setCurrentState = useSetAtom(editorState); 27 | 28 | const currSelected = useAtomValue(currentSelected); 29 | 30 | const setAlertMsg = useSetAtom(alert); 31 | 32 | const setTransitionTracker = useSetAtom(arrowStates); 33 | 34 | const setSaveFSM = useSetAtom(saveFSMAtom); 35 | const saveFSM = useAtomValue(saveFSMAtom); 36 | 37 | // Dock Items 38 | // Each item has a name, icon, and onclick function 39 | // The condition array is used to determine the background color of 40 | // that item's button when it is not active and active respectively 41 | const DockItems = [ 42 | { 43 | name: "Displace", 44 | condition: [currentState != "grab", currentState == "grab"], 45 | icon: ( 46 | 51 | ), 52 | onclick: () => { 53 | currentState == "grab" 54 | ? setCurrentState("nil") 55 | : setCurrentState("grab"); 56 | }, 57 | }, 58 | { 59 | name: "Add", 60 | condition: [currentState != "create", currentState == "create"], 61 | icon: ( 62 | 67 | ), 68 | onclick: () => { 69 | currentState == "create" 70 | ? setCurrentState("nil") 71 | : setCurrentState("create"); 72 | }, 73 | }, 74 | { 75 | name: "Delete", 76 | condition: [currentState != "delete", currentState == "delete"], 77 | icon: ( 78 | 83 | ), 84 | onclick: () => { 85 | currentState == "delete" 86 | ? setCurrentState("nil") 87 | : setCurrentState("delete"); 88 | }, 89 | }, 90 | { 91 | name: "Controls", 92 | condition: [currentState != "settings", currentState == "settings"], 93 | icon: ( 94 | 99 | ), 100 | 101 | onclick: () => { 102 | if (currSelected != "nil") { 103 | currentState == "settings" 104 | ? setCurrentState("nil") 105 | : setCurrentState("settings"); 106 | } else { 107 | setAlertMsg("You must select a State to view its Settings!"); 108 | setTimeout(() => setAlertMsg("nil"), 3000); 109 | } 110 | }, 111 | }, 112 | { 113 | name: "Connect", 114 | condition: [currentState != "connect", currentState == "connect"], 115 | icon: ( 116 | 121 | ), 122 | onclick: () => { 123 | if (currentState == "connect") setCurrentState("nil"); 124 | else { 125 | setCurrentState("connect"); 126 | setTransitionTracker(undefined); 127 | } 128 | }, 129 | }, 130 | 131 | { 132 | name: "Save FSM", 133 | condition: [!saveFSM, saveFSM], 134 | icon: ( 135 | 140 | ), 141 | onclick: () => setSaveFSM(!saveFSM), 142 | }, 143 | 144 | { 145 | name: "Welcome", 146 | condition: [currentState != "welcome", currentState == "welcome"], 147 | icon: ( 148 | 153 | ), 154 | onclick: () => { 155 | currentState == "welcome" 156 | ? setCurrentState("nil") 157 | : setCurrentState("welcome"); 158 | }, 159 | }, 160 | ]; 161 | 162 | return ( 163 |
164 |
165 | {/* Dock Items */} 166 | {DockItems.map((item) => ( 167 |
178 | {item.icon} 179 |

180 | {item.name} 181 |

182 |
183 | ))} 184 |
185 |
186 | ); 187 | }; 188 | 189 | export default Dock; 190 | -------------------------------------------------------------------------------- /src/components/Editor.tsx: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { Stage, Layer, Group, Circle, Text, Arrow } from "react-konva"; 3 | import { 4 | editorState, 5 | currentSelected, 6 | arrowStates, 7 | arrows, 8 | saveFSMAtom, 9 | recentStateSave, 10 | start_state, 11 | alert, 12 | } from "../lib/backend"; 13 | import { useAtom, useAtomValue, useSetAtom } from "jotai"; 14 | import { Nodes } from "../lib/backend"; 15 | import { useRef, useState, useEffect } from "react"; 16 | import clsx from "clsx"; 17 | import { Check, X } from "lucide-react"; 18 | 19 | const Editor = () => { 20 | const currentEditorState = useAtomValue(editorState); 21 | 22 | // Konva Layer Reference 23 | let layerRef = useRef(null); 24 | 25 | // Konva Stage Ref 26 | let stageRef = useRef(null); 27 | 28 | const [nodeList, updateNodeList] = useAtom(Nodes); 29 | 30 | const [currSelected, setCurrSelected] = useAtom(currentSelected); 31 | 32 | const [transitionTracker, setTransitionTracker] = useAtom(arrowStates); 33 | const [transitions, updateTransitions] = useAtom(arrows); 34 | 35 | const [trNameEditor, setTrNameEditor] = useState([ 36 | false, 37 | undefined, 38 | undefined, 39 | ]); // Will have [show/hide, current text, transition text id] 40 | 41 | const [showSaveFSM, setShowSaveFSM] = useAtom(saveFSMAtom); 42 | 43 | const [saveFSM, setSaveFSM] = useState([1, "", ".png"]); // Pop settings for downloading FSM 44 | 45 | const [recentStateControlSaved, setRecentStateControlSaved] = 46 | useAtom(recentStateSave); 47 | 48 | const [startState, setStartState] = useAtom(start_state); 49 | 50 | const setAlertMsg = useSetAtom(alert); 51 | 52 | // Every time a state's controls are changed(size), it's transition arrows should also be updated 53 | useEffect(() => { 54 | if (recentStateControlSaved == "nil") return; 55 | else { 56 | // Update the transition arrow's positions 57 | for (let i = 0; i < transitions.length; i++) { 58 | if ( 59 | transitions[i].to == recentStateControlSaved || 60 | transitions[i].from == recentStateControlSaved 61 | ) { 62 | const newPoints = getPoints(transitions[i].from, transitions[i].to); 63 | transitions[i].points = newPoints; 64 | const arrow = layerRef.current.findOne(`#tr${transitions[i].id}`); 65 | arrow.points(newPoints); 66 | continue; 67 | } 68 | } 69 | 70 | // If node is a start state, additionally update it's start arrow as well 71 | if (recentStateControlSaved == startState) { 72 | const startArrow = layerRef.current.findOne(`#startarrow`); 73 | const startNode = layerRef.current.findOne(`#${startState}`); 74 | 75 | const nodeRadius = startNode.radius(); 76 | 77 | const points = [-nodeRadius / 1.5, 0, nodeRadius - 5, 0]; 78 | 79 | startArrow.x(-1 * (nodeRadius + 40)); 80 | startArrow.points(points); 81 | } 82 | 83 | setRecentStateControlSaved("nil"); 84 | } 85 | }, [recentStateControlSaved]); 86 | 87 | // Handle Creating Nodes by clicking 88 | function handleEditorClick(e: any) { 89 | // Return if not in create mode, and deselects if a node is selected 90 | if (currentEditorState != "create") { 91 | if (currSelected != "nil") { 92 | const selectedNode = layerRef.current.findOne(`#${currSelected}`); 93 | selectedNode.to({ 94 | duration: 0.1, 95 | strokeWidth: 0, 96 | easing: Konva.Easings.EaseInOut, 97 | }); 98 | setCurrSelected("nil"); 99 | } 100 | return; 101 | } 102 | 103 | const group = e.target.getStage().findOne("Group"); 104 | if (!group) return; 105 | 106 | const clickPos = group.getRelativePointerPosition(); 107 | // if node is the first one, then make it the starting state 108 | if (nodeList.length == 0) { 109 | setStartState("0"); 110 | } 111 | updateNodeList((nodes: Node[]) => [ 112 | ...nodes, 113 | { 114 | x: clickPos.x, 115 | y: clickPos.y, 116 | radius: 35, 117 | fill: "#ffffff80", 118 | id: nodes.length, 119 | strokeWidth: 0, 120 | strokeColor: "#ffffff", 121 | name: `q${nodes.length}`, 122 | type: nodeList.length == 0 ? "initial" : "intermediate", 123 | transitions: [], 124 | }, 125 | ]); 126 | } 127 | 128 | // Handle Node Deletion/Selection 129 | function handleNodeClick(id) { 130 | // If editor state is set to delete mode, remove the state 131 | const clickedNode = layerRef.current.findOne(`#${id}`); 132 | 133 | if (currentEditorState == "delete") { 134 | const clickedGroup = layerRef.current.findOne(`#g${id}`); 135 | clickedGroup.destroy(); // Delete the Node 136 | 137 | transitions.forEach((tr) => { 138 | let tre = null; 139 | let trText = null; 140 | if (tr && (tr.from == id || tr.to == id)) { 141 | tre = layerRef.current.findOne(`#tr${tr.id}`); 142 | tre.destroy(); // Delete the arrow 143 | 144 | trText = layerRef.current.findOne(`#trtext${tr.id}`); 145 | trText.destroy(); // Also delete the Label of the transition 146 | 147 | // Delete the transition for the other node participating in the state 148 | if (tr.from == id) { 149 | const aliveNodeTransitions = nodeList[tr.to].transitions; 150 | 151 | for (let i = 0; i < aliveNodeTransitions.length; i++) { 152 | if (aliveNodeTransitions[i].trId == tr.id) { 153 | nodeList[tr.to].transitions.splice(i, 1); 154 | } 155 | } 156 | } else { 157 | const aliveNodeTransitions = nodeList[tr.from].transitions; 158 | 159 | for (let i = 0; i < aliveNodeTransitions.length; i++) { 160 | if (aliveNodeTransitions[i].trId == tr.id) { 161 | nodeList[tr.from].transitions.splice(i, 1); 162 | } 163 | } 164 | } 165 | 166 | transitions[tr.id] = undefined; // remove the arrow entry from the array 167 | } 168 | }); 169 | 170 | updateTransitions(transitions); 171 | 172 | // Update the nodeList store 173 | nodeList[id] = undefined; 174 | updateNodeList(nodeList); 175 | 176 | // If the deleted Node is the one currently selected 177 | // Then deselect it 178 | if (currSelected == id) setCurrSelected("nil"); 179 | 180 | return; 181 | } 182 | 183 | // If current editor state is connect 184 | if (currentEditorState == "connect") { 185 | // Track which two states are clicked on 186 | if (transitionTracker == undefined) { 187 | setTransitionTracker(id); 188 | return; 189 | } else { 190 | // Checks if a transition between selected state(s) already exists to prevent dupe transitions 191 | for (const tr of nodeList[transitionTracker].transitions) { 192 | if (tr.to != null && tr.to == id) { 193 | setTransitionTracker(undefined); 194 | setAlertMsg("This transition already exists!"); 195 | setTimeout(() => setAlertMsg("nil"), 3000); 196 | return; 197 | } 198 | } 199 | 200 | const points = getPoints(transitionTracker, id); 201 | 202 | const newTransition = { 203 | id: transitions.length, 204 | from: transitionTracker, 205 | to: id, 206 | points: points, 207 | stroke: "#ffffffe6", 208 | strokeWidth: 2, 209 | fill: "#ffffffe6", 210 | name: `transition ${transitions.length + 1}`, 211 | tension: transitionTracker == id ? 1 : 0.5, 212 | }; 213 | 214 | transitions.push(newTransition); 215 | updateTransitions(transitions); 216 | 217 | // Add this transition to the corresponding node 218 | nodeList[transitionTracker].transitions.push({ 219 | from: undefined, 220 | to: id, 221 | trId: transitions.length - 1, 222 | }); 223 | 224 | nodeList[id].transitions.push({ 225 | from: transitionTracker, 226 | to: undefined, 227 | trId: transitions.length - 1, 228 | }); 229 | updateNodeList(nodeList); 230 | setTransitionTracker(undefined); 231 | return; 232 | } 233 | } 234 | 235 | // Draw a stroke around the selected node 236 | clickedNode.to({ 237 | duration: 0.1, 238 | strokeWidth: 2, 239 | easing: Konva.Easings.EaseInOut, 240 | }); 241 | 242 | // Deselected the old node if any available 243 | if (currSelected != "nil") { 244 | const oldNode = layerRef.current.findOne(`#${currSelected}`); 245 | 246 | oldNode.to({ 247 | duration: 0.1, 248 | strokeWidth: 0, 249 | easing: Konva.Easings.EaseInOut, 250 | }); 251 | } 252 | 253 | // If same node is clicked, toggle selection 254 | if (currSelected == id) { 255 | setCurrSelected("nil"); 256 | } else { 257 | // Update value of current selected 258 | setCurrSelected(id); 259 | } 260 | } 261 | 262 | // Handle Updating Node Positions when dragged around 263 | function handleNodeDrag(id) { 264 | const draggedNode = layerRef.current.findOne(`#g${id}`); 265 | 266 | nodeList[id].x = draggedNode.x(); 267 | nodeList[id].y = draggedNode.y(); 268 | updateNodeList(nodeList); 269 | 270 | // Update position of arrows if they exist 271 | if (nodeList[id].transitions.length > 0) { 272 | nodeList[id].transitions.forEach((tr) => { 273 | let points = []; 274 | 275 | if (tr.from == undefined) points = getPoints(id, tr.to); 276 | 277 | if (tr.to == undefined) points = getPoints(tr.from, id); 278 | 279 | // Update points in the global store 280 | transitions[tr.trId].points = points; 281 | updateTransitions(transitions); 282 | 283 | // Update position on the editor 284 | const arr = layerRef.current.findOne(`#tr${tr.trId}`); 285 | arr.points(points); 286 | 287 | // Update Position of text 288 | const trText = layerRef.current.findOne(`#trtext${tr.trId}`); 289 | trText.x(points[2] - 3 * transitions[tr.trId].name.length); 290 | trText.y(points[3] - 30); 291 | }); 292 | } 293 | } 294 | 295 | // Generate Points for drawing transition arrow 296 | function getPoints(id1, id2) { 297 | if (id1 == id2) { 298 | // Self-loop 299 | const node = layerRef.current.findOne(`#g${id1}`); 300 | const x = node.x(); 301 | const y = node.y(); 302 | const radius = node.children[0].radius(); 303 | const offset = 30; 304 | 305 | const points = [ 306 | x - radius / 1.5, 307 | y - radius, // Start point (left of the node) 308 | x, 309 | y - radius - 2 * offset, // Control point (top) 310 | x + radius / 1.5, 311 | y - radius, // End point (right of the node) 312 | ]; 313 | 314 | return points; 315 | } 316 | 317 | const clickedGroup = layerRef.current.findOne(`#g${id2}`); 318 | 319 | const startNode = layerRef.current.findOne(`#g${id1}`); 320 | 321 | const dx = clickedGroup.x() - startNode.x(); 322 | const dy = clickedGroup.y() - startNode.y(); 323 | let angle = Math.atan2(-dy, dx); 324 | 325 | const startRadius = startNode.children[0].radius() + 10; 326 | const endRadius = clickedGroup.children[0].radius() + 10; 327 | 328 | const start = [ 329 | startNode.x() + -startRadius * Math.cos(angle + Math.PI), 330 | startNode.y() + startRadius * Math.sin(angle + Math.PI), 331 | ]; 332 | 333 | const end = [ 334 | clickedGroup.x() + -endRadius * Math.cos(angle), 335 | clickedGroup.y() + endRadius * Math.sin(angle), 336 | ]; 337 | 338 | const midpoint = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2]; 339 | 340 | const dist = Math.sqrt((start[0] - end[0]) ** 2 + (start[1] - end[1]) ** 2); 341 | 342 | const subpoint2 = 343 | start[0] < end[0] 344 | ? [midpoint[0], midpoint[1] - 0.3 * dist] 345 | : [midpoint[0], midpoint[1] + 0.3 * dist]; 346 | 347 | const points = [ 348 | start[0], 349 | start[1], 350 | subpoint2[0], 351 | subpoint2[1], 352 | end[0], 353 | start[0] < end[0] ? end[1] - 20 : end[1] + 20, 354 | ]; 355 | 356 | return points; 357 | } 358 | 359 | // Handle Zoom control for the editor 360 | function handleWheel(e) { 361 | e.evt.preventDefault(); 362 | 363 | const stage = stageRef.current; 364 | const oldScale = stage.scaleX(); 365 | const pointer = stage.getPointerPosition(); 366 | 367 | const mousePointTo = { 368 | x: (pointer.x - stage.x()) / oldScale, 369 | y: (pointer.y - stage.y()) / oldScale, 370 | }; 371 | 372 | // how to scale? Zoom in? Or zoom out? 373 | let direction = e.evt.deltaY > 0 ? 1 : -1; 374 | 375 | // when we zoom on trackpad, e.evt.ctrlKey is true 376 | // in that case lets revert direction 377 | if (e.evt.ctrlKey) { 378 | direction = -direction; 379 | } 380 | 381 | const scaleBy = 1.01; 382 | const newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy; 383 | 384 | stage.scale({ x: newScale, y: newScale }); 385 | 386 | const newPos = { 387 | x: pointer.x - mousePointTo.x * newScale, 388 | y: pointer.y - mousePointTo.y * newScale, 389 | }; 390 | stage.position(newPos); 391 | } 392 | 393 | return ( 394 | <> 395 | 403 | 404 | 405 | {nodeList.map( 406 | (node) => 407 | node && ( 408 | handleNodeClick(node.id)} 417 | onDragMove={() => handleNodeDrag(node.id)} 418 | > 419 | 428 | 429 | {/* If state is final, draw an extra outer circle */} 430 | {node.type == "final" && ( 431 | 440 | )} 441 | 442 | 452 | 453 | {/* If state is initial, draw an incoming arrow */} 454 | {node.type == "initial" && ( 455 | 466 | )} 467 | 468 | ) 469 | )} 470 | {/* Transition Arrows */} 471 | {transitions.map( 472 | (transition) => 473 | transition && ( 474 | 475 | {/* Transition arrow object */} 476 | 485 | {/* Add a Label to the middle of the arrow */} 486 | 495 | currentEditorState != "create" && 496 | currentEditorState != "delete" && 497 | setTrNameEditor([true, transition.name, transition.id]) 498 | } 499 | /> 500 | 501 | ) 502 | )} 503 | 504 | 505 | 506 | 507 | {/* Popup window to edit name of transition */} 508 | 509 | 510 | { 515 | setTrNameEditor([true, e.target.value, trNameEditor[2]]); 516 | }} 517 | className="text-white font-github text-base px-2 border border-border-bg hover:border-input-active focus:border-2 focus:border-blue-500 transition-all ease-in-out outline-none w-full h-10 rounded-lg" 518 | /> 519 | 520 | 526 | 527 | 553 | 554 | 555 | 556 | {/* Popup for Save FSM */} 557 | 558 | 559 | 572 | 573 | { 579 | setSaveFSM([saveFSM[0], e.target.value]); 580 | }} 581 | className="text-white font-github text-base px-2 border border-border-bg hover:border-input-active focus:border-2 focus:border-blue-500 transition-all ease-in-out outline-none w-full h-10 rounded-lg" 582 | /> 583 | 584 | 593 | 594 | 622 | 623 | 624 | 625 | ); 626 | }; 627 | 628 | // Transition Name Editor 629 | function TransitionNameEditor(props: { 630 | children: ReactHTMLElement; 631 | showVar: boolean; 632 | }) { 633 | return ( 634 |
643 | {props.children} 644 |
645 | ); 646 | } 647 | 648 | export default Editor; 649 | -------------------------------------------------------------------------------- /src/components/Error.tsx: -------------------------------------------------------------------------------- 1 | const Error = () => { 2 | return ( 3 |
4 |

5 | Welp! 😭 6 |

7 |
8 |

9 | This site only works on a Laptop or Desktop 10 |

11 |
12 | ); 13 | }; 14 | 15 | export default Error; 16 | -------------------------------------------------------------------------------- /src/components/Settings.tsx: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { CircleX, CircleCheck } from "lucide-react"; 3 | import { 4 | editorState, 5 | currentSelected, 6 | Nodes, 7 | recentStateSave, 8 | start_state, 9 | alert, 10 | } from "../lib/backend"; 11 | import { useAtomValue, useSetAtom, useAtom } from "jotai"; 12 | import { useState, useEffect } from "react"; 13 | import clsx from "clsx"; 14 | import type { Atom } from "jotai"; 15 | 16 | const Settings = () => { 17 | const [currentEditorState, changeEditorState] = useAtom(editorState); 18 | 19 | const [nodeList, updateNodeList] = useAtom(Nodes); 20 | 21 | const currSelected = useAtomValue(currentSelected); 22 | 23 | const [stateName, setStateName] = useState(""); 24 | const [stateColor, setStateColor] = useState(""); 25 | const [stateType, setStateType] = useState(""); 26 | 27 | const setRecentStateChange = useSetAtom(recentStateSave); 28 | 29 | const [startState, setStartState] = useAtom(start_state); 30 | 31 | const setAlert = useSetAtom(alert); 32 | 33 | function setDefaultSettingsValues() { 34 | if (currSelected != "nil") { 35 | setStateName(nodeList[currSelected].name); 36 | setStateColor(nodeList[currSelected].fill.substring(0, 7)); 37 | setStateType(nodeList[currSelected].type); 38 | } 39 | } 40 | 41 | function saveSettingsToNode() { 42 | if (currSelected != "nil") { 43 | if (nodeList[currSelected].type != stateType) { 44 | if (stateType == "initial" && startState != "nil") { 45 | setAlert("There can only be one Initial State"); 46 | setTimeout(() => setAlert("nil"), 3000); 47 | return; 48 | } 49 | 50 | if (stateType != "initial") { 51 | nodeList[currSelected].type = stateType; 52 | if (currSelected == startState) setStartState("nil"); 53 | } else if (stateType == "initial" && startState == "nil") { 54 | nodeList[currSelected].type = stateType; 55 | if (currSelected == startState) setStartState(currSelected); 56 | } 57 | 58 | // Update radius of node after changing its name 59 | nodeList[currSelected].radius = 60 | 2 * nodeList[currSelected].name.length + 61 | nodeList[currSelected].radius; 62 | } 63 | 64 | nodeList[currSelected].fill = stateColor + "80"; 65 | 66 | if (nodeList[currSelected].name != stateName) { 67 | nodeList[currSelected].name = stateName; 68 | setRecentStateChange(currSelected); 69 | } 70 | updateNodeList(nodeList); 71 | } 72 | } 73 | 74 | useEffect(setDefaultSettingsValues, [currSelected]); 75 | 76 | return ( 77 |
86 | {/* Settings Window */} 87 |
96 | 97 |

98 | State Name 99 |

100 | setStateName(e.target.value)} 105 | className="text-white font-github text-base px-2 border border-border-bg hover:border-input-active focus:border-2 focus:border-blue-500 transition-all ease-in-out outline-none w-full h-10 rounded-lg" 106 | /> 107 |
108 | 109 | 110 |

111 | State Color 112 |

113 | setStateColor(e.target.value)} 117 | className="rounded-lg border-3 border-border-bg" 118 | /> 119 |
120 | 121 | 122 |

123 | State Type 124 |

125 | 136 | 137 |
138 | 139 | 150 | 151 |
152 | 153 | 164 |
165 | 166 | 167 | 177 | 178 | 188 | 189 |
190 |
191 | ); 192 | }; 193 | 194 | export default Settings; 195 | -------------------------------------------------------------------------------- /src/components/Welcome.tsx: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { 3 | ArrowUpRight, 4 | ChevronRight, 5 | ChevronLeft, 6 | MoveRight, 7 | } from "lucide-react"; 8 | import { editorState } from "../lib/backend"; 9 | import { useAtom } from "jotai"; 10 | import clsx from "clsx"; 11 | import TutorialContent from "../lib/content"; 12 | import { useState } from "react"; 13 | 14 | export default function Welcome() { 15 | const [currentEditorState, setCurrentEditorState] = useAtom(editorState); 16 | 17 | const [displayIndex, setDisplayIndex] = useState(0); 18 | const [showAuthor, setShowAuthor] = useState(false); 19 | 20 | function nextItem() { 21 | if (displayIndex + 1 < TutorialContent.length) 22 | setDisplayIndex(displayIndex + 1); 23 | else setShowAuthor(true); 24 | } 25 | 26 | function prevItem() { 27 | console.log("working!", displayIndex); 28 | if (displayIndex - 1 >= 0 && !showAuthor) setDisplayIndex(displayIndex - 1); 29 | else if (showAuthor) setShowAuthor(false); 30 | } 31 | 32 | return ( 33 |
42 | {/* Settings Window */} 43 |
52 | 53 | 54 | 55 |
56 | 62 | 68 |
69 | 79 |
80 |
81 | ); 82 | } 83 | 84 | // Pages for the Welcom Popup 85 | function Intro(props: { show: boolean }) { 86 | const profileImg = "https://avatars.githubusercontent.com/karthik-saiharsh"; 87 | return ( 88 |
96 | Karthik Saiharsh 101 |

102 | Karthik Saiharsh 103 |

104 |

105 | Hey! I'm Karthik, the creator of FSM Engine. I'm super glad you took 106 | time to check this out :-) 107 |
108 | If you like what I have done, leave a start on my repo! Spread the word! 109 | Tell your friends about FSM Engine! 110 |
111 | If you feel something isn't right, open an issue on github and I'll try 112 | my best to work on it. 113 |
114 | If you are interested to contribute, and help the project grow, feel 115 | free to open a pull request. Would love to see the project grow! 116 |

117 | 118 | 122 | 123 |
124 | ); 125 | } 126 | 127 | // Tutorial Pages 128 | // PS: I know I don't have to use 6 functions to display the tutorials, i'll change it later 129 | 130 | function Tutorial(props: { index: number; show: boolean }) { 131 | return ( 132 |
140 |

141 | {TutorialContent[props.index].title} 142 |

143 | {TutorialContent[props.index].type == "vid" ? ( 144 | 154 | ) : ( 155 | 156 | )} 157 |

158 | {TutorialContent[props.index].content} 159 |

160 | {props.index > 0 && ( 161 |

162 | Watch the above video for better clarity 163 |

164 | )} 165 |
166 | ); 167 | } 168 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | @font-face { 4 | font-family: "github"; 5 | src: url("assets/github_font.ttf"); 6 | } 7 | 8 | @theme { 9 | --font-github: github; 10 | --color-primary-bg: #1e1e1e; 11 | --color-secondary-bg: #2c2c2c; 12 | 13 | --color-border-bg: #414141; 14 | 15 | --color-primary-fg: #171717; 16 | --color-secondary-fg: #e5e9f0; 17 | --color-teritiary-fg: #eceff4; 18 | 19 | --color-input-active: #b1b1b1; 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/backend.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { atom } from "jotai"; 3 | 4 | type nodeTransition = { 5 | from: string; 6 | to: string; 7 | name: string; 8 | trID: string; 9 | }; 10 | 11 | type Node = { 12 | x: number; 13 | y: number; 14 | radius: number; 15 | fill: string; 16 | id: number; 17 | strokeWidth: number; 18 | strokeColor: string; 19 | name: string; 20 | type: "initial" | "final" | "intermediate"; 21 | transitions: nodeTransition[]; 22 | }; 23 | 24 | type Arrow = { 25 | x: number; 26 | y: number; 27 | points: number[]; 28 | stroke: string; 29 | strokeWidth: string; 30 | }; 31 | 32 | // Store for current editor state 33 | export const editorState = atom("welcome"); 34 | 35 | // Store for State Machine's States 36 | export const Nodes: Node[] = atom([]); 37 | 38 | // Store for Keeping track of currently active selected state 39 | export const currentSelected = atom("nil"); 40 | 41 | // Alert Message 42 | export const alert = atom("nil"); 43 | 44 | // Store for State transitions 45 | export const arrows = atom([]); 46 | 47 | // Store for tracking connections 48 | export const arrowStates = atom(undefined); 49 | 50 | // Store to manage visibility of save actions editor 51 | export const saveFSMAtom = atom(false); 52 | 53 | // Track the node whose controls were changed most recently 54 | export const recentStateSave = atom("nil"); 55 | 56 | // Keep track of starting state 57 | export const start_state = atom("nil"); 58 | -------------------------------------------------------------------------------- /src/lib/content.ts: -------------------------------------------------------------------------------- 1 | const TutorialContent = [ 2 | { 3 | title: "Welcome", 4 | src: "fsm-engine.svg", 5 | content: `Welcome to FSM Engine! A Figma Like Editor to Draw Finite State Machines. 6 | This short tutorial will help you familiarize yourself with the editor.`, 7 | type: "img", 8 | }, 9 | 10 | { 11 | title: "Displace", 12 | src: "https://www.youtube.com/embed/gCczmitItvs?si=7Hg3M_vvWMxebGst", 13 | content: `The Displace function allows you to move/displace the entire canvas. To 14 | use it, simply choose "Displace" then left-click and drag anywhere on 15 | the canvas.`, 16 | type: "vid", 17 | }, 18 | 19 | { 20 | title: "Move", 21 | src: "https://www.youtube.com/embed/INyTezJ5w8Y?si=a583BlEeUFyyeV_C", 22 | content: `Move used to be a mode which you had to select to move/drag states. Now you can 23 | move/drag states across the canvas by simply left-clicking and dragging them.`, 24 | type: "vid", 25 | }, 26 | 27 | { 28 | title: "Add", 29 | src: "https://www.youtube.com/embed/cPdCChUnGAg?si=oLbBjO4RB2lJC5u-", 30 | content: `Add, as the name suggests lets you add new states to your project. 31 | Choose "Add" and left-click anywhere on the canvas.`, 32 | type: "vid", 33 | }, 34 | 35 | { 36 | title: "Delete", 37 | src: "https://www.youtube.com/embed/VNAN27cCGUY?si=DnGpzma27Ehb5sft", 38 | content: `Delete, as the name suggests lets you remove states from your project. 39 | Choose "Delete" and left-click on the State you wish to remove.`, 40 | type: "vid", 41 | }, 42 | 43 | { 44 | title: "Controls", 45 | src: "https://www.youtube.com/embed/1iL7gwwC5ec?si=IVzeDad8opLtctGu", 46 | content: `Controls window allows you to fine-tune specific properties of a State 47 | such as its name, color and type. Select a state whose properties you wish to modify 48 | and choose "Controls" from the Dock.`, 49 | type: "vid", 50 | }, 51 | 52 | { 53 | title: "Connect", 54 | src: "https://www.youtube.com/embed/10JQ934gm48?si=x2IFPXLTy3sLi6-A", 55 | content: `"Connect" Mode enables you to draw state transition arrows. When in Connect Mode, 56 | click on one state and then another. An arrow is drawn between the two. Clicking on the 57 | same state twice draws a self-loop. To edit the labels simply click on the label of any transition 58 | a popup appears which has the option to let you edit the transition label`, 59 | type: "vid", 60 | }, 61 | ]; 62 | 63 | export default TutorialContent; 64 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App.tsx"; 5 | 6 | createRoot(document.getElementById("root")!).render( 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2022", 5 | "useDefineForClassFields": true, 6 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "verbatimModuleSyntax": true, 14 | "moduleDetection": "force", 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "erasableSyntaxOnly": true, 23 | "noFallthroughCasesInSwitch": true, 24 | "noUncheckedSideEffectImports": true 25 | }, 26 | "include": ["src"] 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2023", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "verbatimModuleSyntax": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "erasableSyntaxOnly": true, 21 | "noFallthroughCasesInSwitch": true, 22 | "noUncheckedSideEffectImports": true 23 | }, 24 | "include": ["vite.config.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react-swc"; 3 | import tailwindcss from "@tailwindcss/vite"; 4 | 5 | // https://vite.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), tailwindcss()], 8 | }); 9 | --------------------------------------------------------------------------------