├── FyneApp.toml ├── LICENSE ├── LICENSE-GPL-3.0 ├── README.md ├── assets ├── Icon.png ├── Poppins-Bold.ttf ├── Poppins-BoldItalic.ttf ├── Poppins-Italic.ttf ├── Poppins-Regular.ttf ├── logo.png └── screenshot.png ├── bundled.go ├── go.mod ├── go.sum ├── gui.go ├── internal ├── app │ ├── meta.go │ └── project.go ├── dialogs │ └── wizard.go └── editors │ ├── color.go │ ├── color_test.go │ ├── editabletheme.go │ ├── editors.go │ ├── gui.go │ ├── gui_selector.go │ ├── gui_test.go │ ├── preview.go │ ├── testdata │ ├── container.png │ ├── container.xml │ ├── label.png │ └── label.xml │ └── text.go ├── layout.go ├── main.go ├── project.go ├── recents.go └── theme.go /FyneApp.toml: -------------------------------------------------------------------------------- 1 | [Details] 2 | Icon = "assets/Icon.png" 3 | Name = "Fysion App" 4 | ID = "app.fysion" 5 | Version = "0.0.1" 6 | Build = 8 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Fysion is Copyright 2023 Fyne Labs 2 | 3 | This work is dual-licensed under GPL 3.0 (or any later version) and the Fyne Labs Commercial license. 4 | 5 | `SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-FyneLabsCommercial` 6 | 7 | You may use the contents of this repository under the terms of the GPL 3.0 if you do not hold a commercial license. 8 | Should you be the holder of a Fyne Labs commercial license then its terms superceed the GPL 3.0 usage. 9 | -------------------------------------------------------------------------------- /LICENSE-GPL-3.0: -------------------------------------------------------------------------------- 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 | # Fysion Tutorials 2 | 3 | This repository contains the code accompanying our video series "Creating an App Builder" 4 | https://www.youtube.com/watch?v=tYHD7OU9xfM&list=PLw-dEUf31ZOrcauGkrLWSSrJTAfMo_gMf&index=2 5 | 6 | ## Trying out the code 7 | 8 | You can try out the code in this repo by simply calling `go run .`. 9 | You will need to have the development tools required for Fyne - check https://docs.fyne.io/started/. 10 | 11 | ![](assets/screenshot.png) 12 | 13 | ## Episodes 14 | 15 | To check out the code from an old episode just `git checkout ` where the tag is one of: 16 | 17 | ### Series 2: 18 | 19 | * **s2e3**: Widget Tree and About Panel 20 | * **s2e2**: Mobile Preview 21 | * **s2e1**: Add and Delete Widgets 22 | 23 | ### Series 1: 24 | 25 | * **ep10**: Editing Widgets and Layouts 26 | * **ep9**: Project Metadata and Recents Storage 27 | * **ep8**: Editor State and File Save 28 | * **ep7**: Theme Editor and Preview 29 | * **ep6**: GUI Files and Unit Tests 30 | * **ep5**: Opening Multiple Files 31 | * **ep4**: File Tree and Editor Layout 32 | * **ep3**: Project Creation Wizard 33 | * **ep2**: Packaging and Folder Open 34 | * **ep1**: Setup, Layout and Theme 35 | 36 | ## License 37 | 38 | This work is dual-licensed under GPL 3.0 (or any later version) and the Fyne Labs Commercial license. 39 | You may use the contents of this repository under the terms of the GPL 3.0 if you do not hold a commercial license. 40 | Should you be the holder of a Fyne Labs commercial license then its terms superceed the GPL 3.0 usage. 41 | 42 | `SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-FyneLabsCommercial` 43 | 44 | ## Contributions 45 | 46 | Contributions to this repository will be gratefully accepted from any individuals who agree to 47 | license their work under the dual license listed above. 48 | -------------------------------------------------------------------------------- /assets/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/assets/Icon.png -------------------------------------------------------------------------------- /assets/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/assets/Poppins-Bold.ttf -------------------------------------------------------------------------------- /assets/Poppins-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/assets/Poppins-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/Poppins-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/assets/Poppins-Italic.ttf -------------------------------------------------------------------------------- /assets/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/assets/Poppins-Regular.ttf -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/assets/logo.png -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/assets/screenshot.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module fysion.app/fysion 2 | 3 | go 1.17 4 | 5 | require fyne.io/fyne/v2 v2.4.4-rc2.0.20240220215900-3c111a2ff7e9 6 | 7 | require ( 8 | fyne.io/x/fyne v0.0.0-20240326131024-3ba9170cc3be 9 | github.com/BurntSushi/toml v1.3.2 10 | ) 11 | 12 | require ( 13 | github.com/jeandeaual/go-locale v0.0.0-20240204043739-672d8d016d9a // indirect 14 | github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect 15 | github.com/rymdport/portal v0.0.0-20231123202536-da45518a87bb // indirect 16 | ) 17 | 18 | require ( 19 | fyne.io/systray v1.10.1-0.20231230205326-d160fd363db9 // indirect 20 | github.com/davecgh/go-spew v1.1.1 // indirect 21 | github.com/fredbi/uri v1.1.0 // indirect 22 | github.com/fsnotify/fsnotify v1.7.0 // indirect 23 | github.com/fyne-io/defyne v0.0.0-20231230190059-2a2e9bc5492b 24 | github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect 25 | github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect 26 | github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect 27 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect 28 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240306074159-ea2d69986ecb // indirect 29 | github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 // indirect 30 | github.com/go-text/typesetting v0.1.0 // indirect 31 | github.com/godbus/dbus/v5 v5.1.0 // indirect 32 | github.com/gopherjs/gopherjs v1.17.2 // indirect 33 | github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect 34 | github.com/pmezard/go-difflib v1.0.0 // indirect 35 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect 36 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect 37 | github.com/stretchr/testify v1.8.4 38 | github.com/yuin/goldmark v1.5.5 // indirect 39 | golang.org/x/image v0.14.0 // indirect 40 | golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect 41 | golang.org/x/net v0.19.0 // indirect 42 | golang.org/x/sys v0.16.0 // indirect 43 | golang.org/x/text v0.14.0 // indirect 44 | gopkg.in/yaml.v3 v3.0.1 // indirect 45 | ) 46 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 21 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 22 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 23 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 24 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 25 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 26 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 27 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 28 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 29 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 30 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 31 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 32 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 33 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 34 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 35 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 36 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 37 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 38 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 39 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 40 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 41 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 42 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 43 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20221208032759-85de2813cf6b/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 44 | fyne.io/fyne/v2 v2.1.0/go.mod h1:c1vwI38Ebd0dAdxVa6H1Pj6/+cK1xtDy61+I31g+s14= 45 | fyne.io/fyne/v2 v2.1.1/go.mod h1:c1vwI38Ebd0dAdxVa6H1Pj6/+cK1xtDy61+I31g+s14= 46 | fyne.io/fyne/v2 v2.3.1/go.mod h1:dl/M+f0r5lJRhy+gdPbmYy97tbu3SWF/RF6/9KXMs+s= 47 | fyne.io/fyne/v2 v2.4.1/go.mod h1:AWM1iPM2YfliduZ4u/kQzP9E6ARIWm0gg+57GpYzWro= 48 | fyne.io/fyne/v2 v2.4.3/go.mod h1:1h3BKxmQYRJlr2g+RGVxedzr6vLVQ/AJmFWcF9CJnoQ= 49 | fyne.io/fyne/v2 v2.4.4-rc2.0.20240220215900-3c111a2ff7e9 h1:rpfQ8hHNDIiT4wYfQKxzjqqCG0iHOwCfUoGRz7W6Zrg= 50 | fyne.io/fyne/v2 v2.4.4-rc2.0.20240220215900-3c111a2ff7e9/go.mod h1:wK5i1NgGLb97RE3N8Z6JjIdMLCLSsdb8+bMwxDhAkLs= 51 | fyne.io/setup v0.0.0-20230325225027-e5de4a66c608/go.mod h1:/Rt9mfVH85WJGbZID+rwOtNj+tul+wSZfz9KtE/Y9TA= 52 | fyne.io/systray v1.10.1-0.20230207085535-4a244dbb9d03/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= 53 | fyne.io/systray v1.10.1-0.20230722100817-88df1e0ffa9a/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= 54 | fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= 55 | fyne.io/systray v1.10.1-0.20231230205326-d160fd363db9 h1:E/gHmMVyk8TuI6JIgNIv/Qu1JABMVFBIkQ8lYRa5gkQ= 56 | fyne.io/systray v1.10.1-0.20231230205326-d160fd363db9/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= 57 | fyne.io/x/fyne v0.0.0-20211027195715-357fb402507f/go.mod h1:qlr7n1wfK8YCzocbhxObiNHVsw4G0WKajBsffojdFHE= 58 | fyne.io/x/fyne v0.0.0-20240326131024-3ba9170cc3be h1:HBAwKsmfTO4r1Ndlksy5oXIxgWiazj0gu2qmhOmkRFU= 59 | fyne.io/x/fyne v0.0.0-20240326131024-3ba9170cc3be/go.mod h1:1pa3ZVIopRWNvfSG4ZrSkcZ3mJ8qoHPZv4PT8/zpn1o= 60 | github.com/ActiveState/termtest/conpty v0.5.0/go.mod h1:LO4208FLsxw6DcNZ1UtuGUMW+ga9PFtX4ntv8Ymg9og= 61 | github.com/Andrew-M-C/go.jsonvalue v1.1.2-0.20211223013816-e873b56b4a84/go.mod h1:oTJGG91FhtsxvUFVwHSvr6zuaTcAuroj/ToxfT7Ox8U= 62 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 63 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 64 | github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 65 | github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 66 | github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= 67 | github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 68 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 69 | github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I= 70 | github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 71 | github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 72 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 73 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 74 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 75 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 76 | github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE= 77 | github.com/benoitkugler/textlayout v0.3.0/go.mod h1:o+1hFV+JSHBC9qNLIuwVoLedERU7sBPgEFcuSgfvi/w= 78 | github.com/benoitkugler/textlayout-testdata v0.1.1/go.mod h1:i/qZl09BbUOtd7Bu/W1CAubRwTWrEXWq6JwMkw8wYxo= 79 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 80 | github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 81 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 82 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 83 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 84 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 85 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 86 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 87 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 88 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 89 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 90 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 91 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 92 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 93 | github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 94 | github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 95 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 96 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 97 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 98 | github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc= 99 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 100 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 101 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 102 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 103 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 104 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 105 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 106 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 107 | github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= 108 | github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= 109 | github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 110 | github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8= 111 | github.com/fredbi/uri v0.1.0/go.mod h1:1xC40RnIOGCaQzswaOvrzvG/3M3F0hyDVb3aO/1iGy0= 112 | github.com/fredbi/uri v1.0.0/go.mod h1:1xC40RnIOGCaQzswaOvrzvG/3M3F0hyDVb3aO/1iGy0= 113 | github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8= 114 | github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4= 115 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 116 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= 117 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 118 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 119 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 120 | github.com/fyne-io/defyne v0.0.0-20231230190059-2a2e9bc5492b h1:+wdz3odAoph7GeLV63xxDRiB2vm2XQXCBvY6iCj9DPI= 121 | github.com/fyne-io/defyne v0.0.0-20231230190059-2a2e9bc5492b/go.mod h1:fVBqmvo6FKXVAbjxHY4w7bPph+czgMZq+DyFkoq7ecw= 122 | github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4= 123 | github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg= 124 | github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504/go.mod h1:gLRWYfYnMA9TONeppRSikMdXlHQ97xVsPojddUv3b/E= 125 | github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a h1:ybgRdYvAHTn93HW79bLiBiJwVL4jVeyGQRZMgImoeWs= 126 | github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY= 127 | github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk= 128 | github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0= 129 | github.com/fyne-io/terminal v0.0.0-20211022214227-ef292b54d29a/go.mod h1:zkqKA2a0sUIR0dxwSE+Pzon9IEuuS1DVo4AyihLROfo= 130 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 131 | github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM= 132 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 133 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 134 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= 135 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 136 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 137 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 138 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 139 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 140 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 141 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20231223183121-56fa3ac82ce7/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 142 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240306074159-ea2d69986ecb h1:S9I8pIVT5JHKDvmI1vQ0qs5fqxzUfhcZm/YbUC/8k1k= 143 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240306074159-ea2d69986ecb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 144 | github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 145 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 146 | github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 h1:VkKnvzbvHqgEfm351rfr8Uclu5fnwq8HP2ximUzJsBM= 147 | github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8/go.mod h1:h29xCucjNsDcYb7+0rJokxVwYAq+9kQ19WiFuBKkYtc= 148 | github.com/go-text/typesetting v0.0.0-20221212183139-1eb938670a1f/go.mod h1:/cmOXaoTiO+lbCwkTZBgCvevJpbFsZ5reXIpEJVh5MI= 149 | github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k= 150 | github.com/go-text/typesetting v0.1.0 h1:vioSaLPYcHwPEPLT7gsjCGDCoYSbljxoHJzMnKwVvHw= 151 | github.com/go-text/typesetting v0.1.0/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI= 152 | github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= 153 | github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= 154 | github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= 155 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 156 | github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= 157 | github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 158 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 159 | github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= 160 | github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= 161 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= 162 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 163 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 164 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 165 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 166 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 167 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 168 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 169 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 170 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 171 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 172 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 173 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 174 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 175 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 176 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 177 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 178 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 179 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 180 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 181 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 182 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 183 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 184 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 185 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 186 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 187 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 188 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 189 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 190 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 191 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 192 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 193 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 194 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 195 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 196 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 197 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 198 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 199 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 200 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 201 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 202 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 203 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 204 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 205 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 206 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 207 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 208 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 209 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 210 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 211 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 212 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 213 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 214 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 215 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 216 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 217 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 218 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 219 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 220 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 221 | github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= 222 | github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= 223 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 224 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 225 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 226 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 227 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 228 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 229 | github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI= 230 | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 231 | github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 232 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 233 | github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY= 234 | github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY= 235 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 236 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 237 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 238 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 239 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 240 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 241 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 242 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 243 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 244 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 245 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 246 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 247 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 248 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 249 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 250 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 251 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 252 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 253 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 254 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 255 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 256 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 257 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 258 | github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= 259 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 260 | github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= 261 | github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI= 262 | github.com/jackmordaunt/icns/v2 v2.2.6/go.mod h1:DqlVnR5iafSphrId7aSD06r3jg0KRC9V6lEBBp504ZQ= 263 | github.com/jeandeaual/go-locale v0.0.0-20240204043739-672d8d016d9a h1:0x2wuPZ+Cq1CmMDRtnrtqRVNoBCiNuJJQODX8jryeFI= 264 | github.com/jeandeaual/go-locale v0.0.0-20240204043739-672d8d016d9a/go.mod h1:ecPgkvIJEbP1gI6YrO2ILUeS3LZxUV5ZuCy8y6K2RvY= 265 | github.com/jezek/xgb v1.0.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= 266 | github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= 267 | github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY= 268 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 269 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 270 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 271 | github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk= 272 | github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= 273 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 274 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 275 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 276 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 277 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 278 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 279 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 280 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 281 | github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc= 282 | github.com/lucor/goinfo v0.9.0/go.mod h1:L6m6tN5Rlova5Z83h1ZaKsMP1iiaoZ9vGTNzu5QKOD4= 283 | github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 284 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 285 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 286 | github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= 287 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 288 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 289 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 290 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 291 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 292 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 293 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 294 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 295 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 296 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 297 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 298 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 299 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 300 | github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 301 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 302 | github.com/nicksnyder/go-i18n/v2 v2.1.2/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPmS3OLqK1niyLxs= 303 | github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= 304 | github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= 305 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 306 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 307 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 308 | github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 309 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 310 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 311 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 312 | github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= 313 | github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= 314 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 315 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 316 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 317 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 318 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 319 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 320 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 321 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 322 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 323 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 324 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 325 | github.com/rymdport/portal v0.0.0-20231123202536-da45518a87bb h1:ejEomhJX7G4p8dFImv2zcQd2Oa0IpWIeB/FUZRnpQKg= 326 | github.com/rymdport/portal v0.0.0-20231123202536-da45518a87bb/go.mod h1:RYYAnv4sssTQ7ceErKl7UD8auER/0yFV7CgmfS/uAD8= 327 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 328 | github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 329 | github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 330 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 331 | github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= 332 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 333 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 334 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 335 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 336 | github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 337 | github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 338 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 339 | github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 340 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 341 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 342 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 343 | github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 344 | github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= 345 | github.com/srwiley/oksvg v0.0.0-20220731023508-a61f04f16b76/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= 346 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE= 347 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= 348 | github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= 349 | github.com/srwiley/rasterx v0.0.0-20210519020934-456a8d69b780/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= 350 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= 351 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= 352 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 353 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 354 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 355 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 356 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 357 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 358 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 359 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 360 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 361 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 362 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 363 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 364 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 365 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 366 | github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg= 367 | github.com/twpayne/go-geom v1.0.0/go.mod h1:RWsl+e3XSahOul/KH2BHCfF0QxSL4RMnMlFw/TNmET0= 368 | github.com/twpayne/go-kml v1.0.0/go.mod h1:LlvLIQSfMqYk2O7Nx8vYAbSLv4K9rjMvLlEdUKWdjq0= 369 | github.com/twpayne/go-polyline v1.0.0/go.mod h1:ICh24bcLYBX8CknfvNPKqoTbe+eg+MX1NPyJmSBo7pU= 370 | github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= 371 | github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg= 372 | github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ= 373 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 374 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 375 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 376 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 377 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 378 | github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 379 | github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 380 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 381 | github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU= 382 | github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 383 | go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 384 | go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 385 | go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= 386 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 387 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 388 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 389 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 390 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 391 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 392 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 393 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 394 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 395 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 396 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 397 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 398 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 399 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 400 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 401 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 402 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 403 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 404 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 405 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 406 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 407 | golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 408 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 409 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 410 | golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 411 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 412 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 413 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 414 | golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 415 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 416 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 417 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 418 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 419 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 420 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 421 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 422 | golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= 423 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 424 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 425 | golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 426 | golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 427 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 428 | golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 429 | golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= 430 | golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A= 431 | golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= 432 | golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= 433 | golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= 434 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 435 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 436 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 437 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 438 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 439 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 440 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 441 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 442 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 443 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 444 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 445 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 446 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 447 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 448 | golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= 449 | golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= 450 | golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= 451 | golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a h1:sYbmY3FwUWCBTodZL1S3JUuOvaW6kM2o+clDzzDNBWg= 452 | golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a/go.mod h1:Ede7gF0KGoHlj822RtphAHK1jLdrcuRBZg0sF1Q+SPc= 453 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 454 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 455 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 456 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 457 | golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 458 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 459 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 460 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 461 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 462 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 463 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 464 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 465 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 466 | golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 467 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 468 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 469 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 470 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 471 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 472 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 473 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 474 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 475 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 476 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 477 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 478 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 479 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 480 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 481 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 482 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 483 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 484 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 485 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 486 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 487 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 488 | golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 489 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 490 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 491 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 492 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 493 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 494 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 495 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 496 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 497 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 498 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 499 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 500 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 501 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 502 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 503 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 504 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 505 | golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 506 | golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 507 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 508 | golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 509 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 510 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 511 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 512 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 513 | golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 514 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 515 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 516 | golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= 517 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 518 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 519 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 520 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 521 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 522 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 523 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 524 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 525 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 526 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 527 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 528 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 529 | golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 530 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 531 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 532 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 533 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 534 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 535 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 536 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 537 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 538 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 539 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 540 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 541 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 542 | golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 543 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 544 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 545 | golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 546 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 547 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 548 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 549 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 550 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 551 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 552 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 553 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 554 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 555 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 556 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 557 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 558 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 559 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 560 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 561 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 562 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 563 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 564 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 565 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 566 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 567 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 568 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 569 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 570 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 571 | golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 572 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 573 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 574 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 575 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 576 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 577 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 578 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 579 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 580 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 581 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 582 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 583 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 584 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 585 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 586 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 587 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 588 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 589 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 590 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 591 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 592 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 593 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 594 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 595 | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 596 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 597 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 598 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 599 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 600 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 601 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 602 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 603 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 604 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 605 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 606 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 607 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 608 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 609 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 610 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 611 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 612 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 613 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 614 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 615 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 616 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 617 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 618 | golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= 619 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 620 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= 621 | golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= 622 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 623 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 624 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 625 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 626 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 627 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 628 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 629 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 630 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 631 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 632 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 633 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 634 | golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 635 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 636 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 637 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 638 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 639 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 640 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 641 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 642 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 643 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 644 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 645 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 646 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 647 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 648 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 649 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 650 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 651 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 652 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 653 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 654 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 655 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 656 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 657 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 658 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 659 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 660 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 661 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 662 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 663 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 664 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 665 | golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 666 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 667 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 668 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 669 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 670 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 671 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 672 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 673 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 674 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 675 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 676 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 677 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 678 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 679 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 680 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 681 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 682 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 683 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 684 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 685 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 686 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 687 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 688 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 689 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 690 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 691 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 692 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 693 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 694 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 695 | golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= 696 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 697 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 698 | golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= 699 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 700 | golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 701 | golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 702 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 703 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 704 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 705 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 706 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 707 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 708 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 709 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 710 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 711 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 712 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 713 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 714 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 715 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 716 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 717 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 718 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 719 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 720 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 721 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 722 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 723 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 724 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 725 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 726 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 727 | google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= 728 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 729 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 730 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 731 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 732 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 733 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 734 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 735 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 736 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 737 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 738 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 739 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 740 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 741 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 742 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 743 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 744 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 745 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 746 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 747 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 748 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 749 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 750 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 751 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 752 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 753 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 754 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 755 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 756 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 757 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 758 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 759 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 760 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 761 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 762 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 763 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 764 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 765 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 766 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 767 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 768 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 769 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 770 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 771 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 772 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 773 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 774 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 775 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 776 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 777 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 778 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 779 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 780 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 781 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 782 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 783 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 784 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 785 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 786 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 787 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 788 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 789 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 790 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 791 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 792 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 793 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 794 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 795 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 796 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 797 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 798 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 799 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 800 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 801 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 802 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 803 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 804 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 805 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 806 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 807 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 808 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 809 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 810 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 811 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 812 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 813 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 814 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 815 | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 816 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 817 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 818 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 819 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 820 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 821 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 822 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 823 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 824 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 825 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 826 | honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4= 827 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 828 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 829 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 830 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 831 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 832 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 833 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 834 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 835 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 836 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 837 | -------------------------------------------------------------------------------- /gui.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net/url" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | 11 | "fyne.io/fyne/v2" 12 | "fyne.io/fyne/v2/canvas" 13 | "fyne.io/fyne/v2/container" 14 | "fyne.io/fyne/v2/data/binding" 15 | "fyne.io/fyne/v2/dialog" 16 | "fyne.io/fyne/v2/driver/desktop" 17 | "fyne.io/fyne/v2/storage" 18 | "fyne.io/fyne/v2/theme" 19 | "fyne.io/fyne/v2/widget" 20 | 21 | "fysion.app/fysion/internal/app" 22 | "fysion.app/fysion/internal/dialogs" 23 | "fysion.app/fysion/internal/editors" 24 | 25 | gui2 "github.com/fyne-io/defyne/pkg/gui" 26 | 27 | xDialog "fyne.io/x/fyne/dialog" 28 | ) 29 | 30 | type gui struct { 31 | win fyne.Window 32 | project *projectBinding 33 | 34 | fileTree binding.URITree 35 | screenTree binding.StringTree 36 | content *container.DocTabs 37 | openTabs map[string]*tabItem 38 | palette *container.AppTabs 39 | explorer *widget.Accordion 40 | } 41 | 42 | type tabItem struct { 43 | editor editors.Editor 44 | tab *container.TabItem 45 | } 46 | 47 | func (g *gui) makeBanner() fyne.CanvasObject { 48 | title := canvas.NewText("App Creator", theme.ForegroundColor()) 49 | title.TextSize = 14 50 | title.TextStyle = fyne.TextStyle{Bold: true} 51 | 52 | g.project.AddListener(binding.NewDataListener(func() { 53 | p := g.project.GetProject() 54 | name := "App Creator" 55 | if p != nil { 56 | name = p.Meta.Details.Name 57 | } 58 | 59 | title.Text = name 60 | title.Refresh() 61 | })) 62 | 63 | home := widget.NewButtonWithIcon("", theme.HomeIcon(), func() {}) 64 | left := container.NewHBox(home, title) 65 | 66 | logo := canvas.NewImageFromResource(resourceLogoPng) 67 | logo.FillMode = canvas.ImageFillContain 68 | 69 | return container.NewStack(container.NewPadded(left), container.NewPadded(logo)) 70 | } 71 | 72 | func (g *gui) makeGUI() fyne.CanvasObject { 73 | top := g.makeBanner() 74 | g.fileTree = binding.NewURITree() 75 | g.screenTree = binding.NewStringTree() 76 | files := widget.NewTreeWithData(g.fileTree, func(branch bool) fyne.CanvasObject { 77 | return widget.NewLabel("filename.jpg") 78 | }, func(data binding.DataItem, branch bool, obj fyne.CanvasObject) { 79 | l := obj.(*widget.Label) 80 | u, _ := data.(binding.URI).Get() 81 | 82 | l.SetText(filterName(u.Name())) 83 | }) 84 | files.OnSelected = func(id widget.TreeNodeID) { 85 | u, err := g.fileTree.GetValue(id) 86 | if err != nil { 87 | dialog.ShowError(err, g.win) 88 | files.Unselect(id) 89 | return 90 | } 91 | 92 | listable, err := storage.CanList(u) 93 | if listable || err != nil { 94 | files.Unselect(id) 95 | return 96 | } 97 | 98 | _, err = g.openFile(u) 99 | if err != nil { 100 | dialog.ShowError(err, g.win) 101 | files.Unselect(id) 102 | } 103 | } 104 | 105 | screens := widget.NewTree( 106 | func(id widget.TreeNodeID) []widget.TreeNodeID { 107 | return g.screenTree.ChildIDs(id) 108 | }, 109 | func(id widget.TreeNodeID) bool { 110 | return len(g.screenTree.ChildIDs((id))) > 0 111 | }, 112 | func(_ bool) fyne.CanvasObject { 113 | return widget.NewLabel("Screen Item") 114 | }, 115 | func(id widget.TreeNodeID, _ bool, obj fyne.CanvasObject) { 116 | l := obj.(*widget.Label) 117 | data, _ := g.screenTree.GetValue(id) 118 | l.SetText(data) 119 | }) 120 | screens.OnSelected = func(id widget.TreeNodeID) { 121 | if strings.Contains(id, "#") { 122 | splits := strings.Split(id, "#") 123 | u, _ := storage.ParseURI(splits[0]) 124 | edit, err := g.openFile(u) 125 | if err != nil { 126 | dialog.ShowError(err, g.win) 127 | files.Unselect(id) 128 | return 129 | } 130 | 131 | pos := strings.LastIndex(splits[1], ":") 132 | ui, ok := edit.(*editors.GUIEditor) 133 | if pos == -1 || !ok { 134 | return 135 | } 136 | id := splits[1][pos+1:] 137 | obj := findObject(ui.RootObject(), id) 138 | if obj != nil { 139 | ui.SelectWidget(obj) 140 | } 141 | } else { 142 | u, _ := storage.ParseURI(id) 143 | _, err := g.openFile(u) 144 | if err != nil { 145 | dialog.ShowError(err, g.win) 146 | screens.Unselect(id) 147 | } 148 | } 149 | } 150 | g.screenTree.AddListener(binding.NewDataListener(screens.Refresh)) 151 | left := widget.NewAccordion( 152 | widget.NewAccordionItem("Screens", screens), 153 | widget.NewAccordionItem("Files", files), 154 | ) 155 | left.Open(0) 156 | left.MultiOpen = true 157 | g.explorer = left 158 | 159 | g.palette = container.NewAppTabs( 160 | container.NewTabItem("App", g.makeAppPalette()), 161 | ) 162 | 163 | home := widget.NewRichTextFromMarkdown(` 164 | # Welcome to Fysion 165 | 166 | Please open a file from the tree on the left`) 167 | 168 | g.content = container.NewDocTabs( 169 | container.NewTabItem("Home", home), 170 | ) 171 | g.content.CloseIntercept = func(item *container.TabItem) { 172 | var u fyne.URI 173 | for child, childItem := range g.openTabs { 174 | if childItem.tab == item { 175 | u, _ = storage.ParseURI(child) 176 | } 177 | } 178 | 179 | if u != nil { 180 | delete(g.openTabs, u.String()) 181 | } 182 | g.content.Remove(item) 183 | } 184 | g.content.OnSelected = func(item *container.TabItem) { 185 | var u fyne.URI 186 | for child, childItem := range g.openTabs { 187 | if childItem.tab == item { 188 | u, _ = storage.ParseURI(child) 189 | g.setPalette(childItem.editor) 190 | } 191 | } 192 | 193 | if u != nil { 194 | files.Select(u.String()) 195 | } 196 | } 197 | 198 | dividers := [3]fyne.CanvasObject{ 199 | widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), 200 | } 201 | objs := []fyne.CanvasObject{g.content, top, left, g.palette, dividers[0], dividers[1], dividers[2]} 202 | return container.New(newFysionLayout(top, left, g.palette, g.content, dividers), objs...) 203 | } 204 | 205 | func (g *gui) makeAppPalette() fyne.CanvasObject { 206 | name := widget.NewEntry() 207 | id := widget.NewEntry() 208 | version := widget.NewEntry() 209 | 210 | g.project.AddListener(binding.NewDataListener(func() { 211 | p := g.project.GetProject() 212 | if p == nil { 213 | return 214 | } 215 | name.OnChanged = nil 216 | id.OnChanged = nil 217 | version.OnChanged = nil 218 | 219 | name.SetText(p.Meta.Details.Name) 220 | id.SetText(p.Meta.Details.ID) 221 | version.SetText(p.Meta.Details.Version) 222 | 223 | saveMeta := func(_ string) { 224 | metaURI, _ := storage.Child(p.Dir, "FyneApp.toml") 225 | 226 | data := p.Meta 227 | data.Details.Name = name.Text 228 | data.Details.ID = id.Text 229 | data.Details.Version = version.Text 230 | 231 | err := app.Save(data, metaURI) 232 | if err != nil { 233 | dialog.ShowError(err, g.win) 234 | } else { 235 | p = app.NewProject(p.Dir) 236 | g.project.SetProject(p) 237 | } 238 | } 239 | 240 | name.OnChanged = saveMeta 241 | id.OnChanged = saveMeta 242 | version.OnChanged = saveMeta 243 | })) 244 | 245 | return widget.NewForm( 246 | widget.NewFormItem("Name", name), 247 | widget.NewFormItem("ID", id), 248 | widget.NewFormItem("Version", version), 249 | ) 250 | } 251 | 252 | func (g *gui) makeMenu(p fyne.Preferences) *fyne.MainMenu { 253 | save := fyne.NewMenuItem("Save", func() { 254 | current := g.content.Selected() 255 | for _, child := range g.openTabs { 256 | if child.tab != current { 257 | continue 258 | } 259 | 260 | err := child.editor.Save() 261 | if err != nil { 262 | dialog.ShowError(err, g.win) 263 | } 264 | break 265 | } 266 | }) 267 | save.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyS, Modifier: fyne.KeyModifierShortcutDefault} 268 | 269 | recent := fyne.NewMenuItem("Recent Projects...", nil) 270 | recents := listRecents(p) 271 | recentItems := make([]*fyne.MenuItem, len(recents)) 272 | for i, r := range recents { 273 | recentItems[i] = fyne.NewMenuItem(r.name, func() { 274 | g.openProject(r.dir) 275 | }) 276 | } 277 | recent.ChildMenu = fyne.NewMenu("Recents", recentItems...) 278 | 279 | about := fyne.NewMenuItem("About", g.showAbout) 280 | file := fyne.NewMenu("File", 281 | fyne.NewMenuItem("Open Project", g.openProjectDialog), 282 | recent, 283 | fyne.NewMenuItemSeparator(), 284 | save, 285 | fyne.NewMenuItemSeparator(), 286 | about, 287 | ) 288 | 289 | return fyne.NewMainMenu(file) 290 | } 291 | 292 | func (g *gui) openFile(u fyne.URI) (editors.Editor, error) { 293 | if item, ok := g.openTabs[u.String()]; ok { 294 | g.content.Select(item.tab) 295 | return item.editor, nil 296 | } 297 | 298 | edit, err := editors.ForURI(u) 299 | if err != nil { 300 | return nil, err 301 | } 302 | g.setPalette(edit) 303 | 304 | if ui, ok := edit.(*editors.GUIEditor); ok { 305 | obj := ui.RootObject() 306 | 307 | g.screenTree.Remove(u.String()) 308 | 309 | g.screenTree.Append(binding.DataTreeRootID, u.String(), u.Name()[:len(u.Name())-9]) 310 | addObjectsToTree(obj, g.screenTree, u, u.String()+"#") 311 | } 312 | 313 | name := filterName(u.Name()) 314 | item := container.NewTabItem(name, edit.Content()) 315 | if g.openTabs == nil { 316 | g.openTabs = make(map[string]*tabItem) 317 | } 318 | g.openTabs[u.String()] = &tabItem{editor: edit, tab: item} 319 | 320 | dirty := edit.Edited() 321 | dirty.AddListener(binding.NewDataListener(func() { 322 | isDirty, _ := dirty.Get() 323 | if isDirty { 324 | item.Text = name + " *" 325 | } else { 326 | item.Text = name 327 | } 328 | g.content.Refresh() 329 | })) 330 | 331 | for _, tab := range g.content.Items { 332 | if tab.Text != name { 333 | continue 334 | } 335 | 336 | // fix tab 337 | for uri, child := range g.openTabs { 338 | if child.tab != tab { 339 | continue 340 | } 341 | 342 | u, _ = storage.ParseURI(uri) 343 | parent, _ := storage.Parent(u) 344 | tab.Text = parent.Name() + string([]rune{filepath.Separator}) + tab.Text 345 | } 346 | 347 | // fix item 348 | parent, _ := storage.Parent(u) 349 | item.Text = parent.Name() + string([]rune{filepath.Separator}) + item.Text 350 | break 351 | } 352 | 353 | g.content.Append(item) 354 | g.content.Select(item) 355 | 356 | return edit, nil 357 | } 358 | 359 | func (g *gui) openProjectDialog() { 360 | dialog.ShowFolderOpen(func(dir fyne.ListableURI, err error) { 361 | if err != nil { 362 | dialog.ShowError(err, g.win) 363 | return 364 | } 365 | if dir == nil { 366 | return 367 | } 368 | 369 | g.openProject(dir) 370 | }, g.win) 371 | } 372 | 373 | func (g *gui) setPalette(e editors.Editor) { 374 | palettes := e.Palettes() 375 | 376 | g.palette.Items = append(g.palette.Items[:1], palettes...) 377 | g.palette.Refresh() 378 | 379 | if len(g.palette.Items) > 1 { 380 | g.palette.SelectIndex(1) 381 | } 382 | } 383 | 384 | func (g *gui) showCreate(w fyne.Window) { 385 | var wizard *dialogs.Wizard 386 | intro := widget.NewLabel(`Here you can create new project! 387 | 388 | Or open an existing one that you created earlier.`) 389 | 390 | open := widget.NewButton("Open Project", func() { 391 | wizard.Hide() 392 | g.openProjectDialog() 393 | }) 394 | recent := widget.NewButton("Recent Projects", func() { 395 | wizard.Push("Recent Projects", g.makeRecents(wizard)) 396 | }) 397 | create := widget.NewButton("Create Project", func() { 398 | wizard.Push("Project Details", g.makeCreateDetail(wizard)) 399 | }) 400 | create.Importance = widget.HighImportance 401 | 402 | buttons := container.NewGridWithColumns(3, open, recent, create) 403 | home := container.NewVBox(intro, buttons) 404 | 405 | wizard = dialogs.NewWizard("Create Project", home) 406 | wizard.Show(w) 407 | wizard.Resize(home.MinSize().AddWidthHeight(40, 80)) //fyne.NewSize(360, 200)) 408 | } 409 | 410 | func (g *gui) makeCreateDetail(wizard *dialogs.Wizard) fyne.CanvasObject { 411 | homeDir, _ := os.UserHomeDir() 412 | parent := storage.NewFileURI(homeDir) 413 | chosen, _ := storage.ListerForURI(parent) 414 | 415 | name := widget.NewEntry() 416 | name.Validator = func(in string) error { 417 | if in == "" { 418 | return errors.New("project name is required") 419 | } 420 | 421 | return nil 422 | } 423 | var dir *widget.Button 424 | dir = widget.NewButton(chosen.Name(), func() { 425 | d := dialog.NewFolderOpen(func(l fyne.ListableURI, err error) { 426 | if err != nil || l == nil { 427 | return 428 | } 429 | 430 | chosen = l 431 | dir.SetText(l.Name()) 432 | }, g.win) 433 | 434 | d.SetLocation(chosen) 435 | d.Show() 436 | }) 437 | 438 | form := widget.NewForm( 439 | widget.NewFormItem("Name", name), 440 | widget.NewFormItem("Parent Directory", dir), 441 | ) 442 | form.OnSubmit = func() { 443 | project, err := createProject(name.Text, chosen) 444 | if err != nil { 445 | dialog.ShowError(err, g.win) 446 | return 447 | } 448 | wizard.Hide() 449 | g.openProject(project) 450 | } 451 | 452 | return form 453 | } 454 | 455 | func (g *gui) makeRecents(wizard *dialogs.Wizard) fyne.CanvasObject { 456 | items := listRecents(fyne.CurrentApp().Preferences()) 457 | return widget.NewList( 458 | func() int { 459 | return len(items) 460 | }, 461 | func() fyne.CanvasObject { 462 | return widget.NewButton("Recent Project", nil) 463 | }, 464 | func(i widget.ListItemID, o fyne.CanvasObject) { 465 | b := o.(*widget.Button) 466 | 467 | b.OnTapped = func() { 468 | wizard.Hide() 469 | g.openProject(items[i].dir) 470 | } 471 | b.SetText(items[i].name) 472 | }, 473 | ) 474 | } 475 | 476 | func filterName(name string) string { 477 | pos := strings.LastIndex(name, ".gui.json") 478 | if pos != -1 && pos == len(name)-9 { 479 | name = name[:len(name)-5] 480 | } 481 | 482 | return name 483 | } 484 | 485 | func addObjectsToTree(obj fyne.CanvasObject, tree binding.StringTree, file fyne.URI, 486 | root string) { 487 | nodeID := fmt.Sprintf(root+":%p", obj) 488 | nodeRoot := root 489 | if root[len(root)-1] == '#' { 490 | nodeRoot = root[:len(root)-1] 491 | } 492 | tree.Append(nodeRoot, nodeID, gui2.NameOf(obj)) 493 | 494 | switch c := obj.(type) { 495 | case *fyne.Container: 496 | for _, o := range c.Objects { 497 | addObjectsToTree(o, tree, file, nodeID) 498 | } 499 | } 500 | } 501 | 502 | func findObject(obj fyne.CanvasObject, id string) fyne.CanvasObject { 503 | myID := fmt.Sprintf("%p", obj) 504 | if myID == id { 505 | return obj 506 | } 507 | 508 | switch c := obj.(type) { 509 | case *fyne.Container: 510 | for _, o := range c.Objects { 511 | ret := findObject(o, id) 512 | if ret != nil { 513 | return ret 514 | } 515 | } 516 | } 517 | 518 | return nil 519 | } 520 | 521 | func (g *gui) showAbout() { 522 | about, _ := url.Parse("https://fysion.app") 523 | fyneLink, _ := url.Parse("https://fyne.io") 524 | text := `A low-code app builder using the Fyne toolkit` 525 | 526 | xDialog.ShowAboutWindow(text, 527 | []*widget.Hyperlink{ 528 | widget.NewHyperlink("Website", about), 529 | widget.NewHyperlink("Fyne.io", fyneLink)}, 530 | fyne.CurrentApp()) 531 | } 532 | -------------------------------------------------------------------------------- /internal/app/meta.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "fyne.io/fyne/v2" 5 | "fyne.io/fyne/v2/storage" 6 | 7 | "github.com/BurntSushi/toml" 8 | ) 9 | 10 | type FyneApp struct { 11 | Website string `toml:",omitempty"` 12 | Details AppDetails 13 | Development map[string]string `toml:",omitempty"` 14 | Release map[string]string `toml:",omitempty"` 15 | } 16 | 17 | type AppDetails struct { 18 | Icon string `toml:",omitempty"` 19 | Name, ID string `toml:",omitempty"` 20 | Version string `toml:",omitempty"` 21 | Build int `toml:",omitempty"` 22 | } 23 | 24 | func Load(u fyne.URI) (data FyneApp, err error) { 25 | r, err := storage.Reader(u) 26 | if err != nil { 27 | return data, err 28 | } 29 | 30 | defer r.Close() 31 | _, err = toml.NewDecoder(r).Decode(&data) 32 | return data, err 33 | } 34 | 35 | func Save(data FyneApp, u fyne.URI) error { 36 | w, err := storage.Writer(u) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | defer w.Close() 42 | return toml.NewEncoder(w).Encode(&data) 43 | } 44 | -------------------------------------------------------------------------------- /internal/app/project.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "fyne.io/fyne/v2" 5 | "fyne.io/fyne/v2/storage" 6 | ) 7 | 8 | type Project struct { 9 | Meta FyneApp 10 | 11 | Dir fyne.ListableURI 12 | } 13 | 14 | func NewProject(dir fyne.ListableURI) *Project { 15 | name := dir.Name() 16 | p := &Project{Dir: dir} 17 | p.Meta.Details.Name = name 18 | 19 | dataURI, err := storage.Child(dir, "FyneApp.toml") 20 | if err != nil { 21 | fyne.LogError("Failed to access toml file path", err) 22 | return p 23 | } 24 | 25 | meta, err := Load(dataURI) 26 | if err != nil { 27 | fyne.LogError("Failed to parse app metadata", err) 28 | return p 29 | } 30 | 31 | p.Meta = meta 32 | return p 33 | } 34 | -------------------------------------------------------------------------------- /internal/dialogs/wizard.go: -------------------------------------------------------------------------------- 1 | package dialogs 2 | 3 | import ( 4 | "fyne.io/fyne/v2" 5 | "fyne.io/fyne/v2/container" 6 | "fyne.io/fyne/v2/dialog" 7 | "fyne.io/fyne/v2/theme" 8 | "fyne.io/fyne/v2/widget" 9 | ) 10 | 11 | type Wizard struct { 12 | title string 13 | stack []fyne.CanvasObject 14 | content *fyne.Container 15 | 16 | d dialog.Dialog 17 | } 18 | 19 | func NewWizard(title string, content fyne.CanvasObject) *Wizard { 20 | w := &Wizard{title: title, stack: []fyne.CanvasObject{content}} 21 | w.content = container.NewStack(content) 22 | return w 23 | } 24 | 25 | func (w *Wizard) Hide() { 26 | w.d.Hide() 27 | } 28 | 29 | func (w *Wizard) Show(win fyne.Window) { 30 | w.d = dialog.NewCustomWithoutButtons(w.title, w.content, win) 31 | 32 | w.d.Show() 33 | } 34 | 35 | func (w *Wizard) Pop() { 36 | if len(w.stack) <= 1 { 37 | return 38 | } 39 | w.stack = w.stack[:len(w.stack)-1] 40 | 41 | w.content.Objects = []fyne.CanvasObject{w.stack[len(w.stack)-1]} 42 | w.content.Refresh() 43 | } 44 | 45 | func (w *Wizard) Push(title string, content fyne.CanvasObject) { 46 | w.stack = append(w.stack, w.wrap(title, content)) 47 | 48 | w.content.Objects = []fyne.CanvasObject{w.stack[len(w.stack)-1]} 49 | w.content.Refresh() 50 | } 51 | 52 | func (w *Wizard) Resize(size fyne.Size) { 53 | if w.d == nil { 54 | return 55 | } 56 | 57 | w.d.Resize(size) 58 | } 59 | 60 | func (w *Wizard) wrap(title string, content fyne.CanvasObject) fyne.CanvasObject { 61 | nav := container.NewHBox( 62 | widget.NewButtonWithIcon("", theme.NavigateBackIcon(), w.Pop), 63 | widget.NewLabel(title)) 64 | 65 | return container.NewBorder(nav, nil, nil, nil, content) 66 | } 67 | -------------------------------------------------------------------------------- /internal/editors/color.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "fmt" 5 | "image/color" 6 | ) 7 | 8 | func colorForHex(s string) color.Color { 9 | var rgb int 10 | _, err := fmt.Sscanf(s, "#%x", &rgb) 11 | if err != nil { 12 | return color.Transparent 13 | } 14 | 15 | b := rgb & 0xff 16 | gg := rgb >> 8 & 0xff 17 | r := rgb >> 16 & 0xff 18 | return color.NRGBA{R: uint8(r), G: uint8(gg), B: uint8(b), A: 0xff} 19 | } 20 | 21 | func hexForColor(c color.Color) string { 22 | ch := color.RGBAModel.Convert(c).(color.RGBA) 23 | return fmt.Sprintf("#%.2x%.2x%.2x", ch.R, ch.G, ch.B) 24 | } 25 | -------------------------------------------------------------------------------- /internal/editors/color_test.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "image/color" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestColorForHex(t *testing.T) { 11 | assert.True(t, colorsMatch(colorForHex("#000000"), color.Black)) 12 | assert.True(t, colorsMatch(colorForHex("#ffffff"), color.White)) 13 | assert.True(t, colorsMatch(colorForHex("#FFffFF"), color.White)) 14 | assert.True(t, colorsMatch(colorForHex("#c0c0c0"), color.Gray{0xc0})) 15 | 16 | assert.False(t, colorsMatch(colorForHex("#000001"), color.Black)) 17 | assert.False(t, colorsMatch(colorForHex("#fffeff"), color.White)) 18 | assert.False(t, colorsMatch(colorForHex("#FEffFF"), color.White)) 19 | } 20 | 21 | func TestHexForColor(t *testing.T) { 22 | assert.Equal(t, "#000000", hexForColor(color.Black)) 23 | assert.Equal(t, "#ffffff", hexForColor(color.White)) 24 | assert.Equal(t, "#c0c0c0", hexForColor(color.Gray{0xc0})) 25 | 26 | assert.NotEqual(t, "#000001", hexForColor(color.Black)) 27 | assert.NotEqual(t, "#fffeff", hexForColor(color.White)) 28 | assert.NotEqual(t, "#FEffFF", hexForColor(color.White)) 29 | } 30 | 31 | func colorsMatch(a, b color.Color) bool { 32 | if a == nil && b == nil { 33 | return true 34 | } 35 | if a == nil || b == nil { 36 | return false 37 | } 38 | 39 | r1, g1, b1, a1 := a.RGBA() 40 | r2, g2, b2, a2 := b.RGBA() 41 | return r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2 42 | } 43 | -------------------------------------------------------------------------------- /internal/editors/editabletheme.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "image/color" 5 | 6 | "fyne.io/fyne/v2" 7 | "fyne.io/fyne/v2/theme" 8 | ) 9 | 10 | type editableTheme struct { 11 | fyne.Theme 12 | variant fyne.ThemeVariant 13 | multiple float32 14 | 15 | dark map[fyne.ThemeColorName]color.Color 16 | light map[fyne.ThemeColorName]color.Color 17 | } 18 | 19 | func newEditableTheme() *editableTheme { 20 | return &editableTheme{ 21 | Theme: theme.DefaultTheme(), 22 | variant: theme.VariantLight, 23 | dark: make(map[fyne.ThemeColorName]color.Color), 24 | light: make(map[fyne.ThemeColorName]color.Color), 25 | multiple: 1, 26 | } 27 | } 28 | 29 | func (e *editableTheme) Color(n fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color { 30 | if e.variant == theme.VariantLight { 31 | if c, ok := e.light[n]; ok { 32 | return c 33 | } 34 | } else { 35 | if c, ok := e.dark[n]; ok { 36 | return c 37 | } 38 | } 39 | return e.Theme.Color(n, e.variant) 40 | } 41 | 42 | func (e *editableTheme) Size(n fyne.ThemeSizeName) float32 { 43 | return e.Theme.Size(n) * e.multiple 44 | } 45 | 46 | func (e *editableTheme) setColor(n fyne.ThemeColorName, v fyne.ThemeVariant, c color.Color) { 47 | if v == theme.VariantLight { 48 | e.light[n] = c 49 | } else { 50 | e.dark[n] = c 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /internal/editors/editors.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | 7 | "fyne.io/fyne/v2" 8 | "fyne.io/fyne/v2/canvas" 9 | "fyne.io/fyne/v2/container" 10 | "fyne.io/fyne/v2/data/binding" 11 | "fyne.io/fyne/v2/widget" 12 | ) 13 | 14 | var extentions = map[string]func(fyne.URI) (Editor, error){ 15 | ".go": makeGo, 16 | ".gui.json": makeGUI, 17 | ".md": makeMarkdown, 18 | ".png": makeImg, 19 | ".txt": makeTxt, 20 | } 21 | 22 | var mimes = map[string]func(fyne.URI) (Editor, error){ 23 | "text/plain": makeTxt, 24 | } 25 | 26 | type Editor interface { 27 | Content() fyne.CanvasObject 28 | Palettes() []*container.TabItem 29 | 30 | Edited() binding.Bool 31 | Save() error 32 | } 33 | 34 | func ForURI(u fyne.URI) (Editor, error) { 35 | name := strings.ToLower(u.Name()) 36 | var matched func(fyne.URI) (Editor, error) 37 | for ext, edit := range extentions { 38 | pos := strings.LastIndex(name, ext) 39 | if pos == -1 || pos != len(name)-len(ext) { 40 | continue 41 | } 42 | 43 | matched = edit 44 | break 45 | } 46 | if matched == nil { 47 | edit, ok := mimes[u.MimeType()] 48 | if !ok { 49 | return nil, errors.New("unable to find editor for file: " + u.Name() + ", mime: " + u.MimeType()) 50 | } 51 | 52 | return edit(u) 53 | } 54 | 55 | return matched(u) 56 | } 57 | 58 | func makeGo(u fyne.URI) (Editor, error) { 59 | // TODO code editor 60 | code, err := makeTxt(u) 61 | if code != nil { 62 | code.(*simpleEditor).content.(*codeEntry).TextStyle = fyne.TextStyle{Monospace: true} 63 | } 64 | 65 | return code, err 66 | } 67 | 68 | func makeImg(u fyne.URI) (Editor, error) { 69 | img := canvas.NewImageFromURI(u) 70 | img.FillMode = canvas.ImageFillContain 71 | return &simpleEditor{content: img}, nil 72 | } 73 | 74 | func makeMarkdown(u fyne.URI) (Editor, error) { 75 | code, err := makeTxt(u) 76 | if code == nil || err != nil { 77 | return nil, err 78 | } 79 | 80 | txt := code.(*simpleEditor).content.(*codeEntry) 81 | txt.TextStyle = fyne.TextStyle{Monospace: true} 82 | txt.Refresh() 83 | 84 | preview := widget.NewRichTextFromMarkdown(txt.Text) 85 | dirty := txt.OnChanged 86 | txt.OnChanged = func(s string) { 87 | preview.ParseMarkdown(s) 88 | dirty(s) 89 | } 90 | code.(*simpleEditor).content = container.NewHSplit(txt, container.NewScroll(preview)) 91 | 92 | return code, err 93 | } 94 | 95 | type simpleEditor struct { 96 | content fyne.CanvasObject 97 | edited binding.Bool 98 | palettes []*container.TabItem 99 | 100 | save func() error 101 | } 102 | 103 | func (s *simpleEditor) Content() fyne.CanvasObject { 104 | return s.content 105 | } 106 | 107 | func (s *simpleEditor) Palettes() []*container.TabItem { 108 | return s.palettes 109 | } 110 | 111 | func (s *simpleEditor) Edited() binding.Bool { 112 | if s.edited == nil { 113 | s.edited = binding.NewBool() 114 | } 115 | 116 | return s.edited 117 | } 118 | 119 | func (s *simpleEditor) Save() error { 120 | if s.save == nil { 121 | return nil 122 | } 123 | 124 | err := s.save() 125 | if err == nil { 126 | s.Edited().Set(false) 127 | } 128 | return err 129 | } 130 | -------------------------------------------------------------------------------- /internal/editors/gui.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "image/color" 7 | "strings" 8 | 9 | "fyne.io/fyne/v2" 10 | "fyne.io/fyne/v2/canvas" 11 | "fyne.io/fyne/v2/container" 12 | "fyne.io/fyne/v2/dialog" 13 | "fyne.io/fyne/v2/layout" 14 | "fyne.io/fyne/v2/storage" 15 | "fyne.io/fyne/v2/theme" 16 | "fyne.io/fyne/v2/widget" 17 | 18 | "github.com/fyne-io/defyne/pkg/gui" 19 | ) 20 | 21 | type GUIEditor struct { 22 | simpleEditor 23 | 24 | root fyne.CanvasObject 25 | tapper *widgetSelector 26 | } 27 | 28 | func (g *GUIEditor) RootObject() fyne.CanvasObject { 29 | return g.root 30 | } 31 | 32 | func (g *GUIEditor) SelectWidget(obj fyne.CanvasObject) { 33 | g.tapper.choose(obj) 34 | } 35 | 36 | func makeGUI(u fyne.URI) (Editor, error) { 37 | r, err := storage.Reader(u) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | defer r.Close() 43 | obj, _, err := gui.DecodeJSON(r) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | save := func() error { 49 | w, err := storage.Writer(u) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | defer w.Close() 55 | return gui.EncodeJSON(obj, make(map[fyne.CanvasObject]map[string]string), w) 56 | } 57 | 58 | var tapper *widgetSelector 59 | th := newEditableTheme() 60 | themer := container.NewThemeOverride(obj, th) 61 | 62 | widgetNames := gui.WidgetClassList() 63 | toAdd := "" 64 | nameList := widget.NewList( 65 | func() int { 66 | return len(widgetNames) 67 | }, 68 | func() fyne.CanvasObject { 69 | return widget.NewLabel("WidgetClass") 70 | }, 71 | func(id widget.ListItemID, obj fyne.CanvasObject) { 72 | class := widgetNames[id] 73 | name := strings.Split(class, ".")[1] 74 | obj.(*widget.Label).SetText(name) 75 | }) 76 | nameList.OnSelected = func(id widget.ListItemID) { 77 | toAdd = widgetNames[id] 78 | } 79 | insert := widget.NewButton("Insert", func() { 80 | if toAdd == "" { 81 | return 82 | } 83 | if _, ok := tapper.chosen.(*fyne.Container); !ok { 84 | dialog.ShowError(errors.New("selected widget must be a container"), fyne.CurrentApp().Driver().AllWindows()[0]) 85 | return 86 | } 87 | 88 | created := gui.CreateNew(toAdd) 89 | tapper.chosen.(*fyne.Container).Add(created) 90 | themer.Refresh() 91 | }) 92 | remove := widget.NewButtonWithIcon("", theme.DeleteIcon(), func() { 93 | if tapper.chosen == nil { 94 | return 95 | } 96 | 97 | root := obj.(*fyne.Container) 98 | c := containerOf(tapper.chosen, root) 99 | if c == nil { 100 | c = root 101 | } 102 | c.Remove(tapper.chosen) 103 | tapper.chosen = nil 104 | }) 105 | remove.Importance = widget.DangerImportance 106 | 107 | widgetType := widget.NewLabel("(select widget)") 108 | widgetInfo := widget.NewForm( 109 | widget.NewFormItem("Type", widgetType), 110 | ) 111 | 112 | desktopBG := canvas.NewRectangle(theme.BackgroundColor()) 113 | tapper = newWidgetSelector(obj, func(obj fyne.CanvasObject) { 114 | widgetType.SetText(gui.NameOf(obj)) 115 | 116 | items := gui.EditorFor(obj, make(map[string]string)) 117 | widgetInfo.Items = widgetInfo.Items[:1] 118 | widgetInfo.Refresh() 119 | widgetInfo.Items = append(widgetInfo.Items, items...) 120 | widgetInfo.Refresh() 121 | }) 122 | 123 | preview := container.NewPadded(themer, tapper) 124 | desktopHolder := container.NewStack(preview) 125 | 126 | // TODO get project title, from project type when we add it 127 | name := "Preview" // g.title.Get() 128 | window := container.NewInnerWindow(name, container.NewStack(desktopBG, preview)) 129 | window.SetPadded(false) 130 | window.Move(fyne.NewPos(20, 56)) 131 | window.CloseIntercept = func() {} 132 | 133 | mobileBG := canvas.NewRectangle(theme.BackgroundColor()) 134 | mobileHolder := container.NewStack() 135 | 136 | desktop := container.NewMultipleWindows(window) 137 | mobile := container.NewCenter(newMobilePreview(mobileHolder, mobileBG)) 138 | 139 | picker := widget.NewSelect([]string{"Desktop", "Smart Phone"}, func(mode string) { 140 | switch mode { 141 | case "Desktop": 142 | desktopHolder.Objects = []fyne.CanvasObject{preview} 143 | mobileHolder.Objects = []fyne.CanvasObject{} 144 | desktopHolder.Refresh() 145 | mobileHolder.Refresh() 146 | 147 | th.multiple = 1 148 | themer.Refresh() 149 | mobile.Hide() 150 | desktop.Show() 151 | default: 152 | desktopHolder.Objects = []fyne.CanvasObject{} 153 | mobileHolder.Objects = []fyne.CanvasObject{preview} 154 | desktopHolder.Refresh() 155 | mobileHolder.Refresh() 156 | 157 | th.multiple = 0.6 158 | themer.Refresh() 159 | mobile.Show() 160 | desktop.Hide() 161 | } 162 | }) 163 | picker.Selected = "Desktop" 164 | mobile.Hide() 165 | 166 | content := container.NewStack(canvas.NewRectangle(color.Gray{Y: 0xee}), container.NewPadded( 167 | container.NewStack(desktop, mobile, container.NewVBox(container.NewHBox(picker))))) 168 | 169 | buttonRow := container.NewBorder(nil, nil, nil, remove, insert) 170 | addRemove := container.NewBorder(nil, buttonRow, nil, nil, nameList) 171 | widgetPanel := container.NewVSplit(widgetInfo, addRemove) 172 | widgetPanel.Offset = 0.7 173 | tabs := []*container.TabItem{container.NewTabItem("Theme", makeThemePalette(themer, th, desktopBG, mobileBG)), 174 | container.NewTabItem("Widget", widgetPanel)} 175 | gui := &GUIEditor{root: obj, tapper: tapper} 176 | gui.content = content 177 | gui.palettes = tabs 178 | gui.save = save 179 | return gui, nil 180 | } 181 | 182 | func makeThemePalette(obj *container.ThemeOverride, th *editableTheme, bg1, bg2 *canvas.Rectangle) fyne.CanvasObject { 183 | form := container.New(layout.NewFormLayout()) 184 | 185 | // use this to ask our inputs to update on theme change 186 | type updatable interface { 187 | update() 188 | } 189 | 190 | updatePreview := func() { 191 | setPreviewTheme(obj, th, bg1, bg2) 192 | } 193 | updateInputs := func() { 194 | for _, i := range form.Objects { 195 | if b, ok := i.(updatable); ok { 196 | b.update() 197 | } 198 | } 199 | } 200 | 201 | var light, dark *widget.Button 202 | light = widget.NewButton("Light", func() { 203 | th.variant = theme.VariantLight 204 | setPreviewTheme(obj, th, bg1, bg2) 205 | updateInputs() 206 | 207 | light.Importance = widget.HighImportance 208 | dark.Importance = widget.MediumImportance 209 | light.Refresh() 210 | dark.Refresh() 211 | }) 212 | light.Importance = widget.HighImportance 213 | dark = widget.NewButton("Dark", func() { 214 | th.variant = theme.VariantDark 215 | setPreviewTheme(obj, th, bg1, bg2) 216 | updateInputs() 217 | 218 | light.Importance = widget.MediumImportance 219 | dark.Importance = widget.HighImportance 220 | light.Refresh() 221 | dark.Refresh() 222 | }) 223 | variants := container.NewGridWithColumns(2, light, dark) 224 | 225 | form.Objects = []fyne.CanvasObject{ 226 | widget.NewRichTextFromMarkdown("## Brand"), layout.NewSpacer(), 227 | widget.NewLabel("Foreground"), newColorButton(theme.ColorNameForeground, th, updatePreview), 228 | widget.NewLabel("Background"), newColorButton(theme.ColorNameBackground, th, updatePreview), 229 | widget.NewLabel("Highlight"), newColorButton(theme.ColorNamePrimary, th, updatePreview), 230 | 231 | widget.NewRichTextFromMarkdown("## Button"), layout.NewSpacer(), 232 | widget.NewLabel("Background"), newColorButton(theme.ColorNameButton, th, updatePreview), 233 | widget.NewLabel("Pressed"), newColorButton(theme.ColorNamePressed, th, updatePreview), 234 | widget.NewLabel("Disabled"), newColorButton(theme.ColorNameDisabledButton, th, updatePreview), 235 | 236 | widget.NewRichTextFromMarkdown("## Widgets"), layout.NewSpacer(), 237 | widget.NewLabel("Hyperlink"), newColorButton(theme.ColorNameHyperlink, th, updatePreview), 238 | widget.NewLabel("Header Bg"), newColorButton(theme.ColorNameHeaderBackground, th, updatePreview), 239 | widget.NewLabel("Input Bg"), newColorButton(theme.ColorNameInputBackground, th, updatePreview), 240 | widget.NewLabel("Input Border"), newColorButton(theme.ColorNameInputBorder, th, updatePreview), 241 | widget.NewLabel("PlaceHolder"), newColorButton(theme.ColorNamePlaceHolder, th, updatePreview), 242 | widget.NewLabel("ScrollBar"), newColorButton(theme.ColorNameScrollBar, th, updatePreview), 243 | widget.NewLabel("Separator"), newColorButton(theme.ColorNameSeparator, th, updatePreview), 244 | 245 | widget.NewRichTextFromMarkdown("## State"), layout.NewSpacer(), 246 | widget.NewLabel("Hover"), newColorButton(theme.ColorNameHover, th, updatePreview), 247 | widget.NewLabel("Focus"), newColorButton(theme.ColorNameFocus, th, updatePreview), 248 | widget.NewLabel("Selection"), newColorButton(theme.ColorNameSelection, th, updatePreview), 249 | widget.NewLabel("Disabled"), newColorButton(theme.ColorNameDisabled, th, updatePreview), 250 | 251 | widget.NewRichTextFromMarkdown("## Other"), layout.NewSpacer(), 252 | widget.NewLabel("Shadow"), newColorButton(theme.ColorNameShadow, th, updatePreview), 253 | widget.NewLabel("Menu Bg"), newColorButton(theme.ColorNameMenuBackground, th, updatePreview), 254 | widget.NewLabel("Overlay Bg"), newColorButton(theme.ColorNameOverlayBackground, th, updatePreview), 255 | widget.NewLabel("Error"), newColorButton(theme.ColorNameError, th, updatePreview), 256 | widget.NewLabel("Success"), newColorButton(theme.ColorNameSuccess, th, updatePreview), 257 | widget.NewLabel("Warning"), newColorButton(theme.ColorNameWarning, th, updatePreview), 258 | } 259 | 260 | return container.NewBorder(variants, nil, nil, nil, container.NewScroll(form)) 261 | } 262 | 263 | type colorButton struct { 264 | widget.BaseWidget 265 | 266 | name fyne.ThemeColorName 267 | theme *editableTheme 268 | 269 | rect *swatch 270 | text *widget.Entry 271 | fn func() 272 | } 273 | 274 | func newColorButton(n fyne.ThemeColorName, th *editableTheme, fn func()) *colorButton { 275 | col := th.Color(n, th.variant) 276 | var rect *swatch 277 | 278 | text := widget.NewEntry() 279 | text.Text = hexForColor(col) 280 | text.OnChanged = func(s string) { 281 | c := colorForHex(s) 282 | 283 | th.setColor(n, th.variant, c) 284 | rect.setColor(c) 285 | fn() 286 | } 287 | 288 | rect = newSwatch(col, string(n), fyne.NewSquareSize(text.MinSize().Height), func(col color.Color) { 289 | th.setColor(n, th.variant, col) 290 | text.SetText(hexForColor(col)) 291 | fn() 292 | }) 293 | 294 | b := &colorButton{rect: rect, text: text, name: n, theme: th, fn: fn} 295 | b.ExtendBaseWidget(b) 296 | return b 297 | } 298 | 299 | func (c *colorButton) CreateRenderer() fyne.WidgetRenderer { 300 | return widget.NewSimpleRenderer(container.NewBorder(nil, nil, c.rect, nil, c.text)) 301 | } 302 | 303 | func (c *colorButton) update() { 304 | col := c.theme.Color(c.name, c.theme.variant) 305 | c.rect.setColor(col) 306 | c.text.SetText(hexForColor(col)) 307 | } 308 | 309 | type swatch struct { 310 | widget.BaseWidget 311 | 312 | r *canvas.Rectangle 313 | fn func(color.Color) 314 | name string 315 | } 316 | 317 | func newSwatch(c color.Color, name string, min fyne.Size, fn func(color.Color)) *swatch { 318 | r := canvas.NewRectangle(c) 319 | r.CornerRadius = theme.InputRadiusSize() 320 | r.SetMinSize(min) 321 | s := &swatch{r: r, fn: fn, name: name} 322 | s.ExtendBaseWidget(s) 323 | return s 324 | } 325 | 326 | func (s *swatch) CreateRenderer() fyne.WidgetRenderer { 327 | return widget.NewSimpleRenderer(s.r) 328 | } 329 | 330 | func (s *swatch) Tapped(_ *fyne.PointEvent) { 331 | title := fmt.Sprintf("Choose %s Color", s.name) 332 | c := dialog.NewColorPicker(title, "", func(col color.Color) { 333 | if col == nil { 334 | return 335 | } 336 | 337 | s.setColor(col) 338 | s.fn(col) 339 | }, fyne.CurrentApp().Driver().AllWindows()[0]) 340 | c.Advanced = true 341 | c.Show() 342 | } 343 | 344 | func (s *swatch) setColor(c color.Color) { 345 | s.r.FillColor = c 346 | s.r.Refresh() 347 | } 348 | -------------------------------------------------------------------------------- /internal/editors/gui_selector.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "image/color" 5 | 6 | "fyne.io/fyne/v2" 7 | "fyne.io/fyne/v2/canvas" 8 | "fyne.io/fyne/v2/container" 9 | "fyne.io/fyne/v2/theme" 10 | "fyne.io/fyne/v2/widget" 11 | ) 12 | 13 | type widgetSelector struct { 14 | widget.BaseWidget 15 | 16 | root, chosen fyne.CanvasObject 17 | cb func(fyne.CanvasObject) 18 | overlay *canvas.Rectangle 19 | } 20 | 21 | func newWidgetSelector(obj fyne.CanvasObject, cb func(fyne.CanvasObject)) *widgetSelector { 22 | overlay := canvas.NewRectangle(color.Transparent) 23 | overlay.StrokeWidth = 2.5 24 | 25 | ret := &widgetSelector{root: obj, cb: cb, overlay: overlay} 26 | ret.ExtendBaseWidget(ret) 27 | return ret 28 | } 29 | 30 | func (w *widgetSelector) CreateRenderer() fyne.WidgetRenderer { 31 | return widget.NewSimpleRenderer(container.NewWithoutLayout(w.overlay)) 32 | } 33 | 34 | func (w *widgetSelector) Resize(s fyne.Size) { 35 | w.BaseWidget.Resize(s) 36 | 37 | w.updateOverlay() 38 | } 39 | 40 | func (w *widgetSelector) Tapped(ev *fyne.PointEvent) { 41 | found := findChild(w.root, ev.Position) 42 | if found == nil { 43 | found = w.root 44 | } 45 | 46 | w.choose(found) 47 | } 48 | 49 | func (w *widgetSelector) choose(o fyne.CanvasObject) { 50 | w.overlay.StrokeColor = theme.PrimaryColor() 51 | w.overlay.Refresh() 52 | 53 | w.chosen = o 54 | w.updateOverlay() 55 | 56 | w.cb(o) 57 | } 58 | 59 | func (w *widgetSelector) updateOverlay() { 60 | if w.chosen == nil { 61 | return 62 | } 63 | 64 | pos := w.chosen.Position() 65 | size := w.chosen.Size() 66 | if w.chosen == w.root { 67 | pos = fyne.NewSquareOffsetPos(-theme.Padding()) 68 | size = size.Add(fyne.NewSquareSize(theme.Padding() * 2)) 69 | } 70 | w.overlay.Move(pos) 71 | w.overlay.Resize(size) 72 | } 73 | 74 | func containerOf(obj fyne.CanvasObject, root *fyne.Container) *fyne.Container { 75 | for _, w := range root.Objects { 76 | if w == obj { 77 | return root 78 | } 79 | 80 | switch c := w.(type) { 81 | case *fyne.Container: 82 | parent := containerOf(obj, c) 83 | if parent != nil { 84 | return parent 85 | } 86 | } 87 | } 88 | 89 | return nil 90 | } 91 | 92 | func findChild(obj fyne.CanvasObject, pos fyne.Position) fyne.CanvasObject { 93 | switch c := obj.(type) { 94 | case *fyne.Container: 95 | for _, w := range c.Objects { 96 | if !inside(w, pos) { 97 | continue 98 | } 99 | 100 | child := findChild(w, pos.Subtract(w.Position())) 101 | if child != nil { 102 | return child 103 | } 104 | 105 | return w 106 | } 107 | } 108 | 109 | return nil 110 | } 111 | 112 | func inside(o fyne.CanvasObject, p fyne.Position) bool { 113 | topLeft := o.Position() 114 | if p.X < topLeft.X || p.Y < topLeft.Y { 115 | return false 116 | } 117 | 118 | size := o.Size() 119 | return p.X < topLeft.X+size.Width && p.Y < topLeft.Y+size.Height 120 | } 121 | -------------------------------------------------------------------------------- /internal/editors/gui_test.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "fyne.io/fyne/v2" 9 | "fyne.io/fyne/v2/container" 10 | "fyne.io/fyne/v2/test" 11 | "fyne.io/fyne/v2/widget" 12 | "github.com/fyne-io/defyne/pkg/gui" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | const ( 17 | containerJSON = `{ 18 | "Object": { 19 | "Type": "*fyne.Container", 20 | "Layout": "VBox", 21 | "Name": "", 22 | "Objects": [ 23 | { 24 | "Type": "*widget.Label", 25 | "Name": "", 26 | "Struct": { 27 | "Hidden": false, 28 | "Text": "Welcome", 29 | "Alignment": 0, 30 | "Wrapping": 0, 31 | "TextStyle": { 32 | "Bold": false, 33 | "Italic": false, 34 | "Monospace": false, 35 | "Symbol": false, 36 | "TabWidth": 0 37 | }, 38 | "Truncation": 0, 39 | "Importance": 0 40 | } 41 | }, 42 | { 43 | "Type": "*widget.Button", 44 | "Name": "", 45 | "Struct": { 46 | "Hidden": false, 47 | "Text": "A button", 48 | "Icon": null, 49 | "Importance": 0, 50 | "Alignment": 0, 51 | "IconPlacement": 0 52 | } 53 | } 54 | ] 55 | } 56 | } 57 | ` 58 | 59 | labelJSON = `{ 60 | "Object": { 61 | "Type": "*widget.Label", 62 | "Name": "", 63 | "Struct": { 64 | "Hidden": false, 65 | "Text": "Welcome", 66 | "Alignment": 0, 67 | "Wrapping": 0, 68 | "TextStyle": { 69 | "Bold": false, 70 | "Italic": false, 71 | "Monospace": false, 72 | "Symbol": false, 73 | "TabWidth": 0 74 | }, 75 | "Truncation": 0, 76 | "Importance": 0 77 | } 78 | } 79 | } 80 | ` 81 | ) 82 | 83 | func TestDecode(t *testing.T) { 84 | test.NewApp() 85 | obj, _, err := gui.DecodeJSON(strings.NewReader(labelJSON)) 86 | assert.Nil(t, err) 87 | 88 | assert.NotNil(t, obj) 89 | l, ok := obj.(*widget.Label) 90 | assert.True(t, ok) 91 | assert.Equal(t, "Welcome", l.Text) 92 | 93 | test.AssertObjectRendersToImage(t, "label.png", l) 94 | test.AssertObjectRendersToMarkup(t, "label.xml", l) 95 | } 96 | 97 | func TestDecode_Container(t *testing.T) { 98 | test.NewApp() 99 | obj, _, err := gui.DecodeJSON(strings.NewReader(containerJSON)) 100 | assert.Nil(t, err) 101 | 102 | assert.NotNil(t, obj) 103 | c, ok := obj.(*fyne.Container) 104 | assert.True(t, ok) 105 | assert.Equal(t, 2, len(c.Objects)) 106 | 107 | l, ok := c.Objects[0].(*widget.Label) 108 | assert.True(t, ok) 109 | assert.Equal(t, "Welcome", l.Text) 110 | b, ok := c.Objects[1].(*widget.Button) 111 | assert.True(t, ok) 112 | assert.Equal(t, "A button", b.Text) 113 | 114 | test.AssertObjectRendersToImage(t, "container.png", c) 115 | test.AssertObjectRendersToMarkup(t, "container.xml", c) 116 | } 117 | 118 | func TestEncode(t *testing.T) { 119 | test.NewApp() 120 | l := widget.NewLabel("Welcome") 121 | w := bytes.NewBuffer(nil) 122 | 123 | err := gui.EncodeJSON(l, nil, w) 124 | assert.Nil(t, err) 125 | 126 | json := w.String() 127 | assert.NotEmpty(t, json) 128 | assert.Equal(t, labelJSON, json) 129 | } 130 | 131 | func TestEncode_Container(t *testing.T) { 132 | test.NewApp() 133 | c := container.NewVBox(widget.NewLabel("Welcome"), 134 | widget.NewButton("A button", func() {})) 135 | w := bytes.NewBuffer(nil) 136 | 137 | err := gui.EncodeJSON(c, nil, w) 138 | assert.Nil(t, err) 139 | 140 | json := w.String() 141 | assert.NotEmpty(t, json) 142 | assert.Equal(t, containerJSON, json) 143 | } 144 | -------------------------------------------------------------------------------- /internal/editors/preview.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "image/color" 5 | 6 | "fyne.io/fyne/v2" 7 | "fyne.io/fyne/v2/canvas" 8 | "fyne.io/fyne/v2/container" 9 | "fyne.io/fyne/v2/theme" 10 | ) 11 | 12 | func newMobilePreview(obj fyne.CanvasObject, bg *canvas.Rectangle) fyne.CanvasObject { 13 | frame := canvas.NewRectangle(color.Transparent) 14 | frame.StrokeColor = color.Black 15 | frame.StrokeWidth = 6 16 | frame.CornerRadius = 32 17 | 18 | bg.CornerRadius = 32 19 | 20 | inset := canvas.NewRectangle(color.Black) 21 | inset.CornerRadius = 5 22 | handle := canvas.NewRectangle(color.Gray{Y: 0x66}) 23 | handle.CornerRadius = 2 24 | 25 | return container.New(mobileLayout{frame: frame, bg: bg, inset: inset, handle: handle, content: obj}, bg, obj, inset, handle, frame) 26 | } 27 | 28 | type mobileLayout struct { 29 | bg, frame, inset, handle *canvas.Rectangle 30 | content fyne.CanvasObject 31 | } 32 | 33 | func (l mobileLayout) Layout(_ []fyne.CanvasObject, size fyne.Size) { 34 | pad := float32(4) 35 | l.frame.Resize(size) 36 | l.bg.Resize(size) 37 | 38 | l.content.Move(fyne.NewPos(pad, 32)) 39 | l.content.Resize(size.SubtractWidthHeight(pad*2, 64)) 40 | 41 | insetSize := fyne.NewSize(80, 30) 42 | l.inset.Move(fyne.NewPos((size.Width-insetSize.Width)/2, 0)) 43 | l.inset.Resize(insetSize) 44 | 45 | handleSize := fyne.NewSize(120, 4) 46 | l.handle.Move(fyne.NewPos((size.Width-handleSize.Width)/2, size.Height-18)) 47 | l.handle.Resize(handleSize) 48 | } 49 | 50 | func (l mobileLayout) MinSize(objects []fyne.CanvasObject) fyne.Size { 51 | return fyne.NewSize(260, 480) 52 | } 53 | 54 | func setPreviewTheme(c *container.ThemeOverride, th fyne.Theme, 55 | desktopBG, mobileBG *canvas.Rectangle) { 56 | c.Theme = th 57 | c.Refresh() 58 | 59 | bgColor := th.Color(theme.ColorNameBackground, theme.VariantDark) 60 | desktopBG.FillColor = bgColor 61 | desktopBG.Refresh() 62 | mobileBG.FillColor = bgColor 63 | mobileBG.Refresh() 64 | } 65 | -------------------------------------------------------------------------------- /internal/editors/testdata/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/internal/editors/testdata/container.png -------------------------------------------------------------------------------- /internal/editors/testdata/container.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome 7 | 8 | 9 | 10 | 11 | 12 | 13 | A button 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /internal/editors/testdata/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fynelabs/Fysion-tutorials/723b7dadc73eecbcb14e8a6286771b6a24ae46f7/internal/editors/testdata/label.png -------------------------------------------------------------------------------- /internal/editors/testdata/label.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Welcome 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /internal/editors/text.go: -------------------------------------------------------------------------------- 1 | package editors 2 | 3 | import ( 4 | "io" 5 | 6 | "fyne.io/fyne/v2" 7 | "fyne.io/fyne/v2/dialog" 8 | "fyne.io/fyne/v2/driver/desktop" 9 | "fyne.io/fyne/v2/storage" 10 | "fyne.io/fyne/v2/widget" 11 | ) 12 | 13 | type codeEntry struct { 14 | widget.Entry 15 | win fyne.Window 16 | 17 | save func() error 18 | } 19 | 20 | func newCodeEntry(w fyne.Window) *codeEntry { 21 | c := &codeEntry{win: w} 22 | c.ExtendBaseWidget(c) 23 | 24 | c.MultiLine = true 25 | return c 26 | } 27 | 28 | func (c *codeEntry) TypedShortcut(s fyne.Shortcut) { 29 | if sh, ok := s.(*desktop.CustomShortcut); ok { 30 | if sh.Modifier == fyne.KeyModifierShortcutDefault && sh.KeyName == fyne.KeyS { 31 | if c.save != nil { 32 | err := c.save() 33 | if err != nil { 34 | dialog.ShowError(err, c.win) 35 | } 36 | } 37 | return 38 | } 39 | } 40 | 41 | c.Entry.TypedShortcut(s) 42 | } 43 | 44 | func makeTxt(u fyne.URI) (Editor, error) { 45 | var code *codeEntry 46 | save := func() error { 47 | return saveTxt(u, code.Text) 48 | } 49 | code = newCodeEntry(fyne.CurrentApp().Driver().AllWindows()[0]) 50 | 51 | r, err := storage.Reader(u) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | defer r.Close() 57 | data, err := io.ReadAll(r) 58 | code.SetText(string(data)) 59 | edit := &simpleEditor{content: code, save: save} 60 | code.OnChanged = func(_ string) { 61 | edit.Edited().Set(true) 62 | } 63 | code.save = edit.Save 64 | 65 | return edit, err 66 | } 67 | 68 | func saveTxt(u fyne.URI, s string) error { 69 | w, err := storage.Writer(u) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | defer w.Close() 75 | _, err = io.WriteString(w, s) 76 | return err 77 | } 78 | -------------------------------------------------------------------------------- /layout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fyne.io/fyne/v2" 5 | "fyne.io/fyne/v2/theme" 6 | ) 7 | 8 | const sideWidth = 220 9 | 10 | type fysionLayout struct { 11 | top, left, right, content fyne.CanvasObject 12 | dividers [3]fyne.CanvasObject 13 | } 14 | 15 | func newFysionLayout(top, left, right, content fyne.CanvasObject, dividers [3]fyne.CanvasObject) fyne.Layout { 16 | return &fysionLayout{top: top, left: left, right: right, content: content, dividers: dividers} 17 | } 18 | 19 | func (l *fysionLayout) Layout(_ []fyne.CanvasObject, size fyne.Size) { 20 | topHeight := l.top.MinSize().Height 21 | l.top.Resize(fyne.NewSize(size.Width, topHeight)) 22 | 23 | l.left.Move(fyne.NewPos(0, topHeight)) 24 | l.left.Resize(fyne.NewSize(sideWidth, size.Height-topHeight)) 25 | 26 | l.right.Move(fyne.NewPos(size.Width-sideWidth, topHeight)) 27 | l.right.Resize(fyne.NewSize(sideWidth, size.Height-topHeight)) 28 | 29 | l.content.Move(fyne.NewPos(sideWidth, topHeight)) 30 | l.content.Resize(fyne.NewSize(size.Width-sideWidth*2, size.Height-topHeight)) 31 | 32 | dividerThickness := theme.SeparatorThicknessSize() 33 | l.dividers[0].Move(fyne.NewPos(0, topHeight)) 34 | l.dividers[0].Resize(fyne.NewSize(size.Width, dividerThickness)) 35 | 36 | l.dividers[1].Move(fyne.NewPos(sideWidth, topHeight)) 37 | l.dividers[1].Resize(fyne.NewSize(dividerThickness, size.Height-topHeight)) 38 | 39 | l.dividers[2].Move(fyne.NewPos(size.Width-sideWidth, topHeight)) 40 | l.dividers[2].Resize(fyne.NewSize(dividerThickness, size.Height-topHeight)) 41 | } 42 | 43 | func (l *fysionLayout) MinSize(objects []fyne.CanvasObject) fyne.Size { 44 | borders := fyne.NewSize( 45 | sideWidth*2, 46 | l.top.MinSize().Height, 47 | ) 48 | 49 | return borders.AddWidthHeight(100, 100) 50 | } 51 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "path/filepath" 7 | 8 | "fyne.io/fyne/v2" 9 | "fyne.io/fyne/v2/app" 10 | "fyne.io/fyne/v2/data/binding" 11 | "fyne.io/fyne/v2/storage" 12 | ) 13 | 14 | func main() { 15 | a := app.NewWithID("app.fysion") 16 | a.Settings().SetTheme(newFysionTheme()) 17 | w := a.NewWindow("Fysion App") 18 | w.SetPadded(false) 19 | w.Resize(fyne.NewSize(1024, 768)) 20 | 21 | ui := &gui{win: w, project: newProjectBinding()} 22 | w.SetContent(ui.makeGUI()) 23 | w.SetMainMenu(ui.makeMenu(a.Preferences())) 24 | ui.project.AddListener(binding.NewDataListener(func() { 25 | p := ui.project.GetProject() 26 | if p != nil { 27 | w.SetTitle("Fysion App: " + p.Meta.Details.Name) 28 | } 29 | })) 30 | 31 | flag.Usage = func() { 32 | fmt.Println("Usage: fysion [project directory]") 33 | } 34 | flag.Parse() 35 | if len(flag.Args()) > 0 { 36 | dirPath := flag.Args()[0] 37 | dirPath, err := filepath.Abs(dirPath) 38 | if err != nil { 39 | fmt.Println("Error resolving project path", err) 40 | return 41 | } 42 | 43 | dirURI := storage.NewFileURI(dirPath) 44 | dir, err := storage.ListerForURI(dirURI) 45 | if err != nil { 46 | fmt.Println("Error opening project", err) 47 | return 48 | } 49 | 50 | ui.openProject(dir) 51 | } else { 52 | ui.showCreate(w) 53 | } 54 | 55 | w.ShowAndRun() 56 | } 57 | -------------------------------------------------------------------------------- /project.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "strings" 8 | 9 | "fyne.io/fyne/v2" 10 | "fyne.io/fyne/v2/data/binding" 11 | "fyne.io/fyne/v2/storage" 12 | "fyne.io/fyne/v2/widget" 13 | "fysion.app/fysion/internal/app" 14 | ) 15 | 16 | func createFile(name string, dir fyne.URI, content string, data ...interface{}) error { 17 | file, err := storage.Child(dir, name) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | w, err := storage.Writer(file) 23 | if err != nil { 24 | return err 25 | } 26 | defer w.Close() 27 | 28 | _, err = io.WriteString(w, fmt.Sprintf(content, data...)) 29 | return err 30 | } 31 | 32 | func createProject(name string, parent fyne.ListableURI) (fyne.ListableURI, error) { 33 | dir, err := storage.Child(parent, name) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | err = storage.CreateListable(dir) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | err = createFile("go.mod", dir, `module %s 44 | 45 | go 1.17 46 | 47 | require fyne.io/fyne/v2 v2.4.1 48 | `, sanitise(name)) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | err = createFile("FyneApp.toml", dir, `[Details] 54 | 55 | Name = "%s" 56 | `, name) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | err = createFile("main.gui.json", dir, `{ 62 | "Type": "*fyne.Container", 63 | "Layout": "VBox", 64 | "Name": "", 65 | "Objects": [ 66 | { 67 | "Type": "*widget.Label", 68 | "Name": "", 69 | "Struct": { 70 | "Hidden": false, 71 | "Text": "Welcome %s!", 72 | "Alignment": 0, 73 | "Wrapping": 0, 74 | "TextStyle": { 75 | "Bold": false, 76 | "Italic": false, 77 | "Monospace": false, 78 | "Symbol": false, 79 | "TabWidth": 0 80 | }, 81 | "Truncation": 0, 82 | "Importance": 0 83 | } 84 | }, 85 | { 86 | "Type": "*widget.Button", 87 | "Name": "", 88 | "Struct": { 89 | "Hidden": false, 90 | "Text": "A button", 91 | "Icon": null, 92 | "Importance": 0, 93 | "Alignment": 0, 94 | "IconPlacement": 0 95 | } 96 | } 97 | ] 98 | } 99 | `, strings.ReplaceAll(name, "\"", "\\\"")) 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | list, _ := storage.ListerForURI(dir) 105 | return list, err 106 | } 107 | 108 | func (g *gui) openProject(dir fyne.ListableURI) { 109 | project := app.NewProject(dir) 110 | g.project.Set(project) 111 | addRecent(project, fyne.CurrentApp().Preferences()) 112 | 113 | // empty the data binding if we had a project loaded 114 | g.fileTree.Set(map[string][]string{}, map[string]fyne.URI{}) 115 | 116 | addFilesToTree(dir, g.fileTree, g.screenTree, binding.DataTreeRootID) 117 | screens := g.screenTree.ChildIDs(binding.DataTreeRootID) 118 | if len(screens) > 0 { 119 | g.explorer.Items[0].Detail.(*widget.Tree).Select(screens[0]) 120 | } else { 121 | g.explorer.CloseAll() 122 | g.explorer.Open(1) 123 | } 124 | } 125 | 126 | func addFilesToTree(dir fyne.ListableURI, tree binding.URITree, screens binding.StringTree, root string) { 127 | items, _ := dir.List() 128 | for _, uri := range items { 129 | name := uri.Name() 130 | if len(name) > 0 && (name[0] == '.' || name == "go.sum") { 131 | continue 132 | } 133 | pos := strings.LastIndex(name, ".gui.go") 134 | if pos != -1 && pos == len(name)-7 { 135 | continue 136 | } 137 | pos = strings.LastIndex(name, ".gui.json") 138 | if pos != -1 && pos == len(name)-9 { 139 | screens.Append(binding.DataTreeRootID, uri.String(), name[:len(name)-9]) 140 | } 141 | 142 | nodeID := uri.String() 143 | tree.Append(root, nodeID, uri) 144 | 145 | isDir, err := storage.CanList(uri) 146 | if err != nil { 147 | log.Println("Failed to check for listing") 148 | } 149 | if isDir { 150 | child, _ := storage.ListerForURI(uri) 151 | addFilesToTree(child, tree, screens, nodeID) 152 | } 153 | } 154 | } 155 | 156 | func sanitise(in string) string { 157 | return strings.ReplaceAll(in, " ", "_") 158 | } 159 | 160 | type projectBinding struct { 161 | binding.Untyped 162 | } 163 | 164 | func newProjectBinding() *projectBinding { 165 | return &projectBinding{Untyped: binding.NewUntyped()} 166 | } 167 | 168 | func (p *projectBinding) GetProject() *app.Project { 169 | proj, err := p.Get() 170 | if proj == nil || err != nil { 171 | return nil 172 | } 173 | 174 | return proj.(*app.Project) 175 | } 176 | 177 | func (p *projectBinding) SetProject(proj *app.Project) { 178 | p.Set(proj) 179 | } 180 | -------------------------------------------------------------------------------- /recents.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "fyne.io/fyne/v2" 7 | "fyne.io/fyne/v2/storage" 8 | "fysion.app/fysion/internal/app" 9 | ) 10 | 11 | type recent struct { 12 | name string 13 | dir fyne.ListableURI 14 | } 15 | 16 | func addRecent(proj *app.Project, p fyne.Preferences) { 17 | items := listRecents(p) 18 | for i, item := range items { 19 | // don't add a duplicate 20 | if item.dir.String() == proj.Dir.String() { 21 | if i == 0 || len(items) == 1 { 22 | return // nothing to re-order 23 | } 24 | 25 | ordered := append([]*recent{item}, items[:i]...) 26 | if i < len(items)-1 { 27 | ordered = append(ordered, items[i+1:]...) 28 | } 29 | 30 | writeRecents(ordered, p) 31 | return 32 | } 33 | 34 | } 35 | 36 | adding := &recent{name: proj.Meta.Details.Name, dir: proj.Dir} 37 | 38 | all := append([]*recent{adding}, items...) 39 | writeRecents(all, p) 40 | } 41 | 42 | func listRecents(p fyne.Preferences) []*recent { 43 | count := p.Int("recent.count") 44 | ret := make([]*recent, count) 45 | 46 | for i := 0; i < count; i++ { 47 | parent := fmt.Sprintf("recent.%d.", i) 48 | n := p.String(parent + "name") 49 | uriStr := p.String(parent + "uri") 50 | u, err := storage.ParseURI(uriStr) 51 | if err != nil { 52 | fyne.LogError("Failed to parse recent URI", err) 53 | continue 54 | } 55 | 56 | dir, _ := storage.ListerForURI(u) 57 | adding := &recent{name: n, dir: dir} 58 | ret[i] = adding 59 | } 60 | return ret 61 | } 62 | 63 | func writeRecents(list []*recent, p fyne.Preferences) { 64 | p.SetInt("recent.count", len(list)) 65 | 66 | for i, r := range list { 67 | parent := fmt.Sprintf("recent.%d.", i) 68 | p.SetString(parent+"name", r.name) 69 | p.SetString(parent+"uri", r.dir.String()) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /theme.go: -------------------------------------------------------------------------------- 1 | //go:generate fyne bundle -o bundled.go assets 2 | 3 | package main 4 | 5 | import ( 6 | "image/color" 7 | 8 | "fyne.io/fyne/v2" 9 | "fyne.io/fyne/v2/theme" 10 | ) 11 | 12 | type fysionTheme struct { 13 | fyne.Theme 14 | } 15 | 16 | func newFysionTheme() fyne.Theme { 17 | return &fysionTheme{Theme: theme.DefaultTheme()} 18 | } 19 | 20 | func (t *fysionTheme) Color(name fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color { 21 | return t.Theme.Color(name, theme.VariantLight) 22 | } 23 | 24 | func (t *fysionTheme) Font(s fyne.TextStyle) fyne.Resource { 25 | if s.Symbol || s.Monospace { 26 | return t.Theme.Font(s) 27 | } 28 | 29 | if s.Bold { 30 | if s.Italic { 31 | return resourcePoppinsBoldItalicTtf 32 | } else { 33 | return resourcePoppinsBoldTtf 34 | } 35 | } 36 | if s.Italic { 37 | return resourcePoppinsItalicTtf 38 | } 39 | return resourcePoppinsRegularTtf 40 | } 41 | 42 | func (t *fysionTheme) Size(name fyne.ThemeSizeName) float32 { 43 | if name == theme.SizeNameText { 44 | return 12 45 | } 46 | 47 | return t.Theme.Size(name) 48 | } 49 | --------------------------------------------------------------------------------