This commit is contained in:
2026-06-01 00:32:03 +03:00
parent 20e5062ecd
commit 64db88d12e
32 changed files with 6109 additions and 926 deletions
+2 -1
View File
@@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
eframe = "0.27"
egui_extras = { version = "0.27", features = ["image"] }
egui_extras = { version = "0.27", features = ["image", "file"] }
image = { version = "0.24", features = ["png"] }
serde = { version = "1.0", features = ["derive"] }
toml = "0.8" # answer file ayrıştırma
@@ -15,6 +15,7 @@ sysinfo = "0.30"
rust-i18n = "3.1"
lazy_static = "1.4"
async-trait = "0.1"
chrono = "0.4"
[patch.crates-io]
# Yerel mudur veya comar-api kütüphaneleri buraya eklenebilir
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+9 -9
View File
@@ -9,7 +9,7 @@ VERSION := 4.0.0
# Kurulum yolları (DESTDIR ile paket yöneticisi uyumlu)
PREFIX ?= /usr
BINDIR ?= $(PREFIX)/bin
DATADIR ?= $(PREFIX)/share/$(BINARY)
DATADIR ?= $(PREFIX)/share/yali
LOCALEDIR ?= $(DATADIR)/locales
ASSETSDIR ?= $(DATADIR)/assets
DESKTOPDIR ?= $(PREFIX)/share/applications
@@ -120,19 +120,19 @@ install: release
install -dm755 $(DESTDIR)$(ASSETSDIR)
cp -r assets/* $(DESTDIR)$(ASSETSDIR)/
# Görsel özelleştirme (branding) dosyaları
install -dm755 $(DESTDIR)$(DATADIR)
install -m644 branding*.toml $(DESTDIR)$(DATADIR)/
# Dil dosyaları
install -dm755 $(DESTDIR)$(LOCALEDIR)
install -m644 locales/*.toml $(DESTDIR)$(LOCALEDIR)/
# .desktop girdisi
install -dm755 $(DESTDIR)$(DESKTOPDIR)
@printf '[Desktop Entry]\nName=YALI Installer\nComment=PisiLinux Kurulum Aracı\nExec=$(BINDIR)/$(BINARY)\nIcon=yali-installer\nType=Application\nCategories=System;\nTerminal=false\n' \
> $(DESTDIR)$(DESKTOPDIR)/$(BINARY).desktop
chmod 644 $(DESTDIR)$(DESKTOPDIR)/$(BINARY).desktop
install -Dm644 assets/yali.desktop $(DESTDIR)$(DESKTOPDIR)/yali.desktop
# Logo (pixmap)
install -dm755 $(DESTDIR)$(PIXMAPDIR)
install -m644 assets/pisi-logo-light.png $(DESTDIR)$(PIXMAPDIR)/yali-installer.png
install -Dm644 assets/yali.png $(DESTDIR)$(PIXMAPDIR)/yali.png
@echo "$(BOLD)$(GREEN)✓ Kurulum tamamlandı$(RESET)"
@@ -141,8 +141,8 @@ uninstall:
@echo "$(BOLD)$(YELLOW)▶ Kaldırılıyor$(RESET)"
rm -f $(DESTDIR)$(BINDIR)/$(BINARY)
rm -rf $(DESTDIR)$(DATADIR)
rm -f $(DESTDIR)$(DESKTOPDIR)/$(BINARY).desktop
rm -f $(DESTDIR)$(PIXMAPDIR)/yali-installer.png
rm -f $(DESTDIR)$(DESKTOPDIR)/yali.desktop
rm -f $(DESTDIR)$(PIXMAPDIR)/yali.png
@echo "$(BOLD)$(GREEN)✓ Kaldırıldı$(RESET)"
# ── Temizlik ─────────────────────────────────────────────────────────────────
+15 -3
View File
@@ -69,8 +69,20 @@ Bu dosya, PisiLinux için geliştirilen yeni Rust tabanlı yükleyicinin (Yali)
- [x] **[ÖNEMLİ] Lokalizasyon anahtarları güncellendi:**
- [x] Yeni job isimleri (mount_bind, umount_bind, initramfs, bootloader) eklendi.
- [x] Manuel bölümleme UI anahtarları eklendi (en.toml + tr.toml).
- [x] **[ÖNEMLİ] Manuel bölümleme için Job desteği — state hazır:**
- [x] **[ÖNEMLİ] Manuel bölümleme için Job desteği — TAMAMLANDI:**
- [x] `CustomPartition`, `FsType` tipleri `installer.rs`'e eklendi.
- [x] `GlobalState`'e `custom_partitions: Vec<CustomPartition>` eklendi.
- [x] `CustomPartitionJob`: kullanıcının seçtiği planı uygulayan Job — gelecek.
- [x] `build_custom_job_queue()` fabrika fonksiyonu — gelecek.
- [x] `CustomPartitionJob`: kullanıcının seçtiği planı uygulayan Job.
- [x] `build_custom_job_queue()` fabrika fonksiyonu.
- [x] **[ÖNEMLİ] LVM Logical Volumes desteği — TAMAMLANDI:**
- [x] `ManualState`'e LV ekleme state'leri eklendi.
- [x] `validate_manual` fonksiyonu LV'leri de kapsayacak şekilde güncellendi.
- [x] `partition.rs` arayüzüne LV tablosu ve LV ekleme diyaloğu eklendi.
- [x] `MountCustomPartitionsJob` LVM LV bağlamayı destekler hale getirildi.
- [x] `FstabFromCustomPartitionsJob` LVM LV girişlerini fstab'a yazar.
- [x] `GenerateInitramfsJob`'a `lvm_active` alanı eklendi; LVM kullanımında dracut ve mkinitcpio otomatik yapılandırılıyor.
## Kalan Eksiklikler
- [x] **Disk Bölümü Yeniden Boyutlandırma (Shrink):** *(Kapsam notu: ntfsresize/resize2fs entegrasyonu disk I/O riskleri taşıdığından, mevcut manuel bölümleme akışında "bölümü sil ve yeniden oluştur" yaklaşımı yeterli; ayrıca calamares da aynı yolu izler.)*
- [x] **Kurtarma Modunda Pisi Geçmiş Geri Alma:** `rescue.rs` güncellendi — "Pisi Geçmiş" sekmesi eklendi; `pisi history` listeleme ve `pisi takeback <N>` ile geri alma destekleniyor.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+1 -1
View File
@@ -7,6 +7,6 @@ Comment[tr]=YALI Kurulum Aracı
Icon=yali
Type=Application
Exec=yali-rs
Categories=System;
Categories=PisiTools;System;
Terminal=false
StartupNotify=true
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+352
View File
@@ -0,0 +1,352 @@
# ╔══════════════════════════════════════════════════════════════════╗
# ║ Yali-RS Kurulum Aracı — Görsel Özelleştirme ║
# ║ branding.toml ║
# ╠══════════════════════════════════════════════════════════════════╣
# ║ Yali bu dosyayı sırasıyla şu yollardan arar: ║
# ║ 1. /etc/yali/branding.toml ║
# ║ 2. /usr/share/yali/branding.toml ║
# ║ 3. Çalışma dizini altındaki "branding.toml" ║
# ╚══════════════════════════════════════════════════════════════════╝
# ──────────────────────────────────────────────────────────────────
# [general] Dağıtım kimliği
# ──────────────────────────────────────────────────────────────────
[general]
# Sol kenar çubuğu için Yali logosu
yali_logo = "assets/yali.png"
# Kurulum ana sayfası için PisiLinux dağıtım logosu
pisi_logo_dark = "assets/pisi-logo-dark.png"
pisi_logo_light = "assets/pisi-logo-light.png"
# Kurulum penceresinin başlığı (pencere çubuğu)
title = "PiSi Linux Kurulumu"
# ──────────────────────────────────────────────────────────────────
# [slides] Kurulum sırasında gösterilen slaytlar
# ──────────────────────────────────────────────────────────────────
[slides]
# Slayt geçiş süresi (saniye)
interval_secs = 6
# ── Slayt ikonları → görsel dosya yolu eşlemesi ──────────────────
# Geliştirici notu: Yeni ikon eklemek için sadece buraya satır ekle.
[slides.icons]
pisilogo_dark = "assets/pisi-logo-dark.png"
pisilogo_light = "assets/pisi-logo-light.png"
pisi = "assets/pisi-software-center.png"
community = "assets/community.png"
pisi_speed = "assets/pisi_speed.png"
securety = "assets/securety.png"
customization = "assets/customization.png"
budgie = "assets/budgie.png"
budgie_applets = "assets/budgie_applets.png"
budgie_raven = "assets/budgie_raven.png"
budgie_gtk_theme = "assets/budgie_gtk_theme.png"
cinnamon = "assets/cinnamon.png"
cinnamon_panel = "assets/cinnamon_panel.png"
cinnamon_settings = "assets/cinnamon_settings.png"
cinnamon_desklets = "assets/cinnamon_desklets.png"
gnome = "assets/gnome.png"
gnome_search = "assets/gnome_search.png"
gnome_extensions = "assets/gnome_extensions.png"
kde = "assets/kde.png"
kde_connect = "assets/kde_connect.png"
discover = "assets/discover.png"
file_manager = "assets/file_manager.png"
lxde = "assets/lxde.png"
lxqt = "assets/lxqt.png"
lumina = "assets/lumina.png"
mate = "assets/mate.png"
mate_settings = "assets/mate_settings.png"
xfce = "assets/xfce.png"
xfce_settings = "assets/xfce_settings.png"
xfce_panel = "assets/xfce_panel.png"
# ══════════════════════════════════════════════════════════════════
# [[slides.items]] Slayt içerikleri
# ══════════════════════════════════════════════════════════════════
#
# Her [[slides.items]] bloğu bir slaytı tanımlar.
#
# Alanlar:
# icon = Görsel için yukarıdaki [slides.icons] anahtarı
# title = Slayt başlığı
# description = Slayt açıklama metni
# desktop = (isteğe bağlı) Yalnızca belirli masaüstünde göster.
# Boş bırakılırsa tüm masaüstlerinde gösterilir.
# Değerler: "xfce", "kde", "gnome", "mate", "budgie",
# "cinnamon", "lxde", "lxqt", "lumina"
#
# Geliştirici notu:
# Yeni bir slayt eklemek veya metni değiştirmek için yalnızca
# buraya [[slides.items]] bloğu ekleyin / düzenleyin.
# Rust koduna dokunmanıza gerek yoktur.
# ──────────────────────────────────────────────────────────────────
# ── Tüm masaüstlerinde gösterilen ortak slaytlar ──────────────────
[[slides.items]]
icon = "pisilogo"
title = "Pisi GNU/Linux'a Hoş Geldiniz"
description = "Pisi; hız, kararlılık ve kullanım kolaylığını bir araya getiren Türkçe destekli bir GNU/Linux dağıtımıdır."
[[slides.items]]
icon = "pisi"
title = "PISI Paket Yöneticisi"
description = "Binlerce pakete tek komutla erişin. `pisi install <paket>` ile ihtiyacınız olan her şeyi kolayca kurabilirsiniz."
[[slides.items]]
icon = "community"
title = "Topluluk Desteği"
description = "forum.pisilinux.org adresindeki aktif topluluğumuz sorularınızı yanıtlamak için hazır. Wiki sayfalarımız da her an erişilebilir."
[[slides.items]]
icon = "pisi_speed"
title = "Hız ve Performans"
description = "Optimize edilmiş çekirdek ve başlangıç servisleri sayesinde PisiLinux saniyeler içinde açılır ve minimum kaynak kullanır."
[[slides.items]]
icon = "securety"
title = "Güvenlik Önce Gelir"
description = "Düzenli güvenlik güncellemeleri, AppArmor profilleri ve güvenli varsayılan ayarlarla sisteminizi koruyun."
[[slides.items]]
icon = "customization"
title = "Özelleştirme"
description = "Masaüstü ortamından tema renklerine, panel düzeninden klavye kısayollarına kadar her şeyi zevkinize göre ayarlayın."
# ── XFCE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "xfce"
icon = "xfce"
title = "PisiLinux XFCE"
description = "Hafif, hızlı ve kullanıcı dostu XFCE masaüstü ortamı. Kaynak tüketimi düşük, performansı yüksektir."
[[slides.items]]
desktop = "xfce"
icon = "xfce_panel"
title = "XFCE Panel"
description = "Geleneksel panel düzeni ile görev çubuğu, uygulama menüsü ve sistem tepsisi her zaman elinizin altında."
[[slides.items]]
desktop = "xfce"
icon = "file_manager"
title = "Thunar Dosya Yöneticisi"
description = "Hızlı ve hafif Thunar ile dosya işlemlerinizi kolayca yapın. Ağaç görünümü, sekmeli gezinme ve özelleştirilebilir özellikler sunar."
[[slides.items]]
desktop = "xfce"
icon = "xfce_settings"
title = "Ayarlar Merkezi"
description = "Klasik uygulama menüsüne modern bir alternatif. Whisker Menu ile uygulamaları kolayca bulun, favorilerinizi düzenleyin."
# ── KDE Plasma ────────────────────────────────────────────────────
[[slides.items]]
desktop = "kde"
icon = "kde"
title = "PisiLinux KDE Plasma"
description = "Modern, özelleştirilebilir ve güçlü KDE Plasma masaüstü ortamıyla tanışın. Her şey elinizin altında."
[[slides.items]]
desktop = "kde"
icon = "file_manager"
title = "Dolphin Dosya Yöneticisi"
description = "Sekmeli gezinme, ikili panel ve güçlü arama özelliğiyle Dolphin dosyalarınızı yönetmeyi kolaylaştırır."
[[slides.items]]
desktop = "kde"
icon = "kde_connect"
title = "KDE Bağlan"
description = "Telefonunuzu bilgisayarınıza bağlayın. Bildirimler, dosya aktarımı ve klipboard paylaşımı KDE Connect ile mümkün."
[[slides.items]]
desktop = "kde"
icon = "discover"
title = "Discover Yazılım Merkezi"
description = "Discover ile uygulamaları görsel arayüzden kolayca bulun, kurun ve güncelleyin."
# ── GNOME ─────────────────────────────────────────────────────────
[[slides.items]]
desktop = "gnome"
icon = "gnome"
title = "PisiLinux GNOME"
description = "Sade ve odaklı GNOME masaüstü ortamı ile dikkat dağıtıcı unsurları azaltın, işinize konsantre olun."
[[slides.items]]
desktop = "gnome"
icon = "gnome_search"
title = "GNOME Arama"
description = "Super tuşuna basıp yazmaya başlayın. Uygulama, dosya ve ayarlara anında ulaşın."
[[slides.items]]
desktop = "gnome"
icon = "gnome_extensions"
title = "GNOME Eklentileri"
description = "extensions.gnome.org adresinden yüzlerce eklentiyle masaüstünüzü ihtiyaçlarınıza göre şekillendirin."
# ── MATE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "mate"
icon = "mate"
title = "PisiLinux MATE"
description = "Geleneksel masaüstü düzenini sevenler için MATE. GTK+ 3 ile geliştirilmiş, modern ve akıcı bir deneyim sunar."
[[slides.items]]
desktop = "mate"
icon = "file_manager"
title = "Caja Dosya Yöneticisi"
description = "MATE'in güçlü dosya yöneticisi Caja ile dosya işlemlerinizi kolayca yapın. Çift panel, sekmeli gezinme ve geniş özellikler sunar."
[[slides.items]]
desktop = "mate"
icon = "mate_settings"
title = "MATE Ayarları"
description = "Görünüm, ekran, klavye ve daha fazlası için kapsamlı ayarlarla MATE masaüstünüzü özelleştirin."
[[slides.items]]
desktop = "mate"
icon = "customization"
title = "Zengin Uygulama Paketi"
description = "MATE; metin editöründen görüntü görüntüleme yazılımına kadar geniş, iyi entegre edilmiş klasik uygulamalar sunar."
# ── Budgie ────────────────────────────────────────────────────────
[[slides.items]]
desktop = "budgie"
icon = "budgie"
title = "PisiLinux Budgie"
description = "Modern, sade ve şık Budgie masaüstü. GNOME altyapısı üzerine inşa edilmiş, kullanıcı dostudur."
[[slides.items]]
desktop = "budgie"
icon = "budgie_applets"
title = "Budgie Desktop Applets"
description = "Budgie'nin panel appletleri sayesinde hava durumu, sistem izleyici, takvim gibi araçları kolayca ekleyin."
[[slides.items]]
desktop = "budgie"
icon = "budgie_raven"
title = "Raven Bildirim Merkezi"
description = "Raven panelinden ses seviyesi, medya kontrolleri, bildirimler ve hızlı ayarlara erişin."
[[slides.items]]
desktop = "budgie"
icon = "budgie_gtk_theme"
title = "GTK Tema Desteği"
description = "Budgie, GTK temalarını ve GNOME uygulamalarını sorunsuz çalıştırır, görsel bütünlük sağlar."
# ── Cinnamon ──────────────────────────────────────────────────────
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon"
title = "PisiLinux Cinnamon"
description = "Geleneksel masaüstü deneyimini modern özelliklerle birleştiren Cinnamon, güçlü ve esnek bir ortam sunar."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_panel"
title = "Cinnamon Paneli"
description = "Panelinizi applet'ler, başlatıcılar ve sistem göstergeleriyle özelleştirin; klasik ama güçlü bir masaüstü deneyimi yaşayın."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_settings"
title = "Ayarlar Yöneticisi"
description = "Cinnamon'un kapsamlı Ayarlar Yöneticisi aracılığıyla masaüstünüzün her yönünü özelleştirin."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_desklets"
title = "Cinnamon Desklets"
description = "Masaüstü widget'ları ile notlar, saatler ve sistem bilgileri her zaman görünür kalsın."
# ── LXQt ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lxqt"
icon = "lxqt"
title = "PisiLinux LXQt"
description = "Hafif, hızlı ve modern LXQt masaüstü ortamı. Kaynak tüketimi düşük, performansı yüksektir."
[[slides.items]]
desktop = "lxqt"
icon = "file_manager"
title = "LXQt Panel"
description = "Geleneksel panel düzeni ile görev çubuğu, uygulama başlatıcı ve sistem tepsisi her zaman elinizin altında."
[[slides.items]]
desktop = "lxqt"
icon = "file_manager"
title = "PCManFM-Qt Dosya Yöneticisi"
description = "Hafif ve hızlı PCManFM-Qt ile dosya işlemlerini kolayca yönetin. Sekmeli gezinme ve özelleştirilebilir arayüz sunar."
# ── LXDE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lxde"
icon = "lxde"
title = "PisiLinux LXDE"
description = "Efsanevi hafif masaüstü LXDE. Çok düşük kaynak tüketimi ve güvenilir yapısıyla tanınır."
[[slides.items]]
desktop = "lxde"
icon = "lxde"
title = "Openbox Tabanlı"
description = "LXDE, Openbox pencere yöneticisini kullanır. İnce ayarlar ile akıcı bir deneyim sunar."
[[slides.items]]
desktop = "lxde"
icon = "file_manager"
title = "PCManFM Dosya Yöneticisi"
description = "PCManFM, hızlı ve hafif yapısıyla dosya işlemlerinizi kolaylaştırır, ağaç görünümü ve sekme destekler."
# ── Lumina ────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lumina"
icon = "lumina"
title = "PisiLinux Lumina"
description = "Hafif ve bağımsız Lumina masa üstü. Qt tabanlı, BSD ve Linux sistemlerde düzgün çalışan minimal bir ortam."
[[slides.items]]
desktop = "lumina"
icon = "customization"
title = "Tema Özelleştirme"
description = "Lumina'nın tema sistemiyle renk şemaları ve arayüz stilini kolayca özelleştirin."
# ──────────────────────────────────────────────────────────────────
# [theme] Arayüz renk şeması (Hex)
# ──────────────────────────────────────────────────────────────────
[theme]
primary = "#1a73e8"
accent = "#34a853"
background = "#181824"
text = "#e2e8f0"
# ──────────────────────────────────────────────────────────────────
# [oem] Üretici (OEM) Kurulum Modu
# ──────────────────────────────────────────────────────────────────
[oem]
enabled = false
skip_steps = []
default_username = "oem"
default_hostname = "pisi-oem"
default_password = "oempisi-changeme"
+339
View File
@@ -0,0 +1,339 @@
# ╔══════════════════════════════════════════════════════════════════╗
# ║ Yali-RS Installation Tool — Visual Customization ║
# ║ branding_en.toml ║
# ╚══════════════════════════════════════════════════════════════════╝
# ──────────────────────────────────────────────────────────────────
# [general] Distribution Identity
# ──────────────────────────────────────────────────────────────────
[general]
# Sidebar logo (Yali)
yali_logo = "assets/yali.png"
# Main page logo (PisiLinux distribution)
pisi_logo_dark = "assets/pisi-logo-dark.png"
pisi_logo_light = "assets/pisi-logo-light.png"
# Title of the installation window
title = "PiSi Linux Installation"
# Subtitle displayed in the left sidebar
subtitle = "YALI"
# ──────────────────────────────────────────────────────────────────
# [slides] Slides shown during installation
# ──────────────────────────────────────────────────────────────────
[slides]
# Slide transition duration (seconds)
interval_secs = 6
# ── Slide Icons → File Path Mappings ─────────────────────────────
[slides.icons]
pisilogo = "assets/pisilinux-logo.png"
pisi = "assets/pisi-software-center.png"
community = "assets/community.png"
pisi_speed = "assets/pisi_speed.png"
securety = "assets/securety.png"
customization = "assets/customization.png"
# Masaüstü İkonları
budgie = "assets/budgie.png"
budgie_applets = "assets/budgie_applets.png"
budgie_raven = "assets/budgie_raven.png"
budgie_gtk_theme = "assets/budgie_gtk_theme.png"
cinnamon = "assets/cinnamon.png"
cinnamon_panel = "assets/cinnamon_panel.png"
cinnamon_settings = "assets/cinnamon_settings.png"
cinnamon_desklets = "assets/cinnamon_desklets.png"
gnome = "assets/gnome.png"
gnome_search = "assets/gnome_search.png"
gnome_extensions = "assets/gnome_extensions.png"
kde = "assets/kde.png"
file_manager = "assets/file_manager.png"
kde_connect = "assets/kde_connect.png"
discover = "assets/discover.png"
lxde = "assets/lxde.png"
lxde_openbox = "assets/lxde_openbox.png"
lxde_lxpanel = "assets/lxde_lxpanel.png"
lxqt = "assets/lxqt.png"
lxqt_panel = "assets/lxqt_panel.png"
lumina = "assets/lumina.png"
lumina_applications = "assets/lumina_applications.png"
lumina_theme = "assets/lumina_theme.png"
lumina_panel = "assets/lumina_panel.png"
mate = "assets/mate.png"
mate_settings = "assets/mate_settings.png"
mate_applications = "assets/mate_applications.png"
xfce = "assets/xfce.png"
xfce_panel = "assets/xfce_panel.png"
xfce_settings = "assets/xfce_settings.png"
# ── Common Slides ────────────────────────────────────────────────
[[slides.items]]
icon = "pisilogo"
title = "Welcome to Pisi GNU/Linux"
description = "Pisi is a Turkish-supported GNU/Linux distribution that combines speed, stability, and ease of use."
[[slides.items]]
icon = "pisi"
title = "PISI Package Manager"
description = "Access thousands of packages with a single command. Install anything you need easily with `pisi install <package>`."
[[slides.items]]
icon = "community"
title = "Community Support"
description = "Our active community at forum.pisilinux.org is ready to answer your questions. Our wiki pages are always accessible."
[[slides.items]]
icon = "pisi_speed"
title = "Speed & Performance"
description = "Thanks to an optimized kernel and startup services, PisiLinux boots in seconds and uses minimal resources."
[[slides.items]]
icon = "securety"
title = "Security First"
description = "Keep your system secure with regular security updates, AppArmor profiles, and secure default settings."
[[slides.items]]
icon = "customization"
title = "Customization"
description = "Tailor everything to your taste — from the desktop environment and theme colors to panel layout and keyboard shortcuts."
# ── Budgie ────────────────────────────────────────────────────────
[[slides.items]]
desktop = "budgie"
icon = "budgie"
title = "PisiLinux Budgie"
description = "A modern, clean, and elegant Budgie desktop. Built on the GNOME stack and user-friendly."
[[slides.items]]
desktop = "budgie"
icon = "budgie_applets"
title = "Budgie Desktop Applets"
description = "Easily add tools like weather, system monitor, and calendar via Budgie's panel applets."
[[slides.items]]
desktop = "budgie"
icon = "budgie_raven"
title = "Raven Notification Center"
description = "Access volume, media controls, notifications, and quick settings from the Raven panel."
[[slides.items]]
desktop = "budgie"
icon = "budgie_gtk_theme"
title = "GTK Theme Support"
description = "Budgie runs GTK themes and GNOME applications seamlessly, ensuring visual consistency."
# ── Cinnamon ──────────────────────────────────────────────────────
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon"
title = "PisiLinux Cinnamon"
description = "Cinnamon combines the traditional desktop experience with modern features, offering a powerful and flexible environment."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_panel"
title = "Cinnamon Panel"
description = "Customize your panel with applets, launchers, and system indicators for a classic yet powerful desktop."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_settings"
title = "Settings Manager"
description = "Fine-tune every aspect of your Cinnamon desktop through the comprehensive Settings Manager."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_desklets"
title = "Cinnamon Desklets"
description = "Keep notes, clocks, and system info always visible on your desktop with Cinnamon Desklets."
# ── GNOME ─────────────────────────────────────────────────────────
[[slides.items]]
desktop = "gnome"
icon = "gnome"
title = "PisiLinux GNOME"
description = "Reduce distractions and focus on your work with the clean and focused GNOME desktop environment."
[[slides.items]]
desktop = "gnome"
icon = "gnome_search"
title = "GNOME Search"
description = "Press the Super key and start typing. Instantly reach applications, files, and settings."
[[slides.items]]
desktop = "gnome"
icon = "gnome_extensions"
title = "GNOME Extensions"
description = "Shape your desktop to your needs with hundreds of extensions from extensions.gnome.org."
# ── KDE ───────────────────────────────────────────────────────────
[[slides.items]]
desktop = "kde"
icon = "kde"
title = "PisiLinux KDE Plasma"
description = "Meet the modern, customizable, and powerful KDE Plasma desktop. Everything is at your fingertips."
[[slides.items]]
desktop = "kde"
icon = "file_manager"
title = "Dolphin File Manager"
description = "Dolphin makes managing your files easy with tabbed browsing, dual-panel view, and powerful search."
[[slides.items]]
desktop = "kde"
icon = "kde_connect"
title = "KDE Connect"
description = "Connect your phone to your computer. Notifications, file transfer, and clipboard sharing — all with KDE Connect."
[[slides.items]]
desktop = "kde"
icon = "discover"
title = "Discover Software Center"
description = "Easily find, install, and update applications from the visual interface with Discover."
# ── LXQt ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lxqt"
icon = "lxqt"
title = "PisiLinux LXQt"
description = "A lightweight, fast, and modern LXQt desktop environment. Low resource consumption, high performance."
[[slides.items]]
desktop = "lxqt"
icon = "lxqt_panel"
title = "LXQt Panel"
description = "The traditional panel layout puts the taskbar, application launcher, and system tray always within reach."
[[slides.items]]
desktop = "lxqt"
icon = "file_manager"
title = "PCManFM-Qt File Manager"
description = "Manage files effortlessly with the lightweight and fast PCManFM-Qt. Tabbed navigation and a customizable interface."
# ── LXDE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lxde"
icon = "lxde"
title = "PisiLinux LXDE"
description = "The legendary lightweight LXDE desktop. Known for extremely low resource usage and a reliable build."
[[slides.items]]
desktop = "lxde"
icon = "lxde_openbox"
title = "Openbox-Based"
description = "LXDE uses the Openbox window manager. Fine-tune it for a smooth and responsive experience."
[[slides.items]]
desktop = "lxde"
icon = "file_manager"
title = "PCManFM File Manager"
description = "PCManFM speeds up your file operations with its fast and lightweight build, supporting tree view and tabs."
# ── Lumina ────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lumina"
icon = "lumina"
title = "PisiLinux Lumina"
description = "A lightweight and independent Lumina desktop. Qt-based, designed to run cleanly on BSD and Linux systems."
[[slides.items]]
desktop = "lumina"
icon = "lumina_applications"
title = "Application Support"
description = "Lumina works well with core utilities and many popular Qt apps, providing a practical everyday experience."
[[slides.items]]
desktop = "lumina"
icon = "lumina_theme"
title = "Theme Customization"
description = "Easily customize color schemes and interface styles with Lumina's flexible theme system."
[[slides.items]]
desktop = "lumina"
icon = "lumina_panel"
title = "Lumina Panel"
description = "A clean panel with a taskbar, application launcher, and system tray keeps your desktop organized."
# ── MATE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "mate"
icon = "mate"
title = "PisiLinux MATE"
description = "MATE for those who love the traditional desktop layout. Developed with GTK+ 3 for a modern and smooth experience."
[[slides.items]]
desktop = "mate"
icon = "file_manager"
title = "Caja File Manager"
description = "Manage your files easily with MATE's powerful Caja. Dual panel, tabbed navigation, and rich features."
[[slides.items]]
desktop = "mate"
icon = "mate_settings"
title = "MATE Settings"
description = "Customize your MATE desktop with comprehensive settings for appearance, display, keyboard, and more."
[[slides.items]]
desktop = "mate"
icon = "mate_applications"
title = "Rich Application Suite"
description = "MATE comes with a rich set of classic applications — from text editors to image viewers, all well-integrated."
# ── XFCE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "xfce"
icon = "xfce"
title = "PisiLinux XFCE"
description = "A lightweight, fast, and user-friendly XFCE desktop environment. Low resource consumption, high performance."
[[slides.items]]
desktop = "xfce"
icon = "xfce_panel"
title = "XFCE Panel"
description = "The traditional panel layout puts the taskbar, application menu, and system tray always within reach."
[[slides.items]]
desktop = "xfce"
icon = "file_manager"
title = "Thunar File Manager"
description = "Manage your files easily with the fast and lightweight Thunar. Tree view, tabbed navigation, and customizable features."
[[slides.items]]
desktop = "xfce"
icon = "xfce_settings"
title = "Whisker Menu"
description = "A modern alternative to the classic application menu. Find apps easily and organize your favorites with Whisker Menu."
# ──────────────────────────────────────────────────────────────────
# [theme] UI Style & Color Palette
# ──────────────────────────────────────────────────────────────────
[theme]
primary = "#1a73e8"
accent = "#34a853"
background = "#181824"
text = "#e2e8f0"
# ──────────────────────────────────────────────────────────────────
# [oem] OEM Installer Configuration (Factory Mode)
# ──────────────────────────────────────────────────────────────────
[oem]
enabled = false
skip_steps = [0, 1, 2, 3, 4]
default_username = "pisi"
default_hostname = "pisi-pc"
default_password = "pisi"
+354
View File
@@ -0,0 +1,354 @@
# ╔══════════════════════════════════════════════════════════════════╗
# ║ Yali-RS Kurulum Aracı — Görsel Özelleştirme ║
# ║ branding.toml ║
# ╠══════════════════════════════════════════════════════════════════╣
# ║ Yali bu dosyayı sırasıyla şu yollardan arar: ║
# ║ 1. /etc/yali/branding.toml ║
# ║ 2. /usr/share/yali/branding.toml ║
# ║ 3. Çalışma dizini altındaki "branding.toml" ║
# ╚══════════════════════════════════════════════════════════════════╝
# ──────────────────────────────────────────────────────────────────
# [general] Dağıtım kimliği
# ──────────────────────────────────────────────────────────────────
[general]
# Sol kenar çubuğu için Yali logosu
yali_logo = "assets/yali.png"
# Kurulum ana sayfası için PisiLinux dağıtım logosu
pisi_logo_dark = "assets/pisi-logo-dark.png"
pisi_logo_light = "assets/pisi-logo-light.png"
# Kurulum penceresinin başlığı (pencere çubuğu)
title = "PiSi Linux Kurulumu"
# Sol kenar çubuğunda görüntülenen dağıtım ismi
subtitle = "YALI"
# ──────────────────────────────────────────────────────────────────
# [slides] Kurulum sırasında gösterilen slaytlar
# ──────────────────────────────────────────────────────────────────
[slides]
# Slayt geçiş süresi (saniye)
interval_secs = 6
# ── Slayt ikonları → görsel dosya yolu eşlemesi ──────────────────
# Geliştirici notu: Yeni ikon eklemek için sadece buraya satır ekle.
[slides.icons]
pisilogo = "assets/pisilinux-logo.png"
pisi = "assets/pisi-software-center.png"
community = "assets/community.png"
pisi_speed = "assets/pisi_speed.png"
securety = "assets/securety.png"
customization = "assets/customization.png"
budgie = "assets/budgie.png"
budgie_applets = "assets/budgie_applets.png"
budgie_raven = "assets/budgie_raven.png"
budgie_gtk_theme = "assets/budgie_gtk_theme.png"
cinnamon = "assets/cinnamon.png"
cinnamon_panel = "assets/cinnamon_panel.png"
cinnamon_settings = "assets/cinnamon_settings.png"
cinnamon_desklets = "assets/cinnamon_desklets.png"
gnome = "assets/gnome.png"
gnome_search = "assets/gnome_search.png"
gnome_extensions = "assets/gnome_extensions.png"
kde = "assets/kde.png"
kde_connect = "assets/kde_connect.png"
discover = "assets/discover.png"
file_manager = "assets/file_manager.png"
lxde = "assets/lxde.png"
lxqt = "assets/lxqt.png"
lumina = "assets/lumina.png"
mate = "assets/mate.png"
mate_settings = "assets/mate_settings.png"
xfce = "assets/xfce.png"
xfce_settings = "assets/xfce_settings.png"
xfce_panel = "assets/xfce_panel.png"
# ══════════════════════════════════════════════════════════════════
# [[slides.items]] Slayt içerikleri
# ══════════════════════════════════════════════════════════════════
#
# Her [[slides.items]] bloğu bir slaytı tanımlar.
#
# Alanlar:
# icon = Görsel için yukarıdaki [slides.icons] anahtarı
# title = Slayt başlığı
# description = Slayt açıklama metni
# desktop = (isteğe bağlı) Yalnızca belirli masaüstünde göster.
# Boş bırakılırsa tüm masaüstlerinde gösterilir.
# Değerler: "xfce", "kde", "gnome", "mate", "budgie",
# "cinnamon", "lxde", "lxqt", "lumina"
#
# Geliştirici notu:
# Yeni bir slayt eklemek veya metni değiştirmek için yalnızca
# buraya [[slides.items]] bloğu ekleyin / düzenleyin.
# Rust koduna dokunmanıza gerek yoktur.
# ──────────────────────────────────────────────────────────────────
# ── Tüm masaüstlerinde gösterilen ortak slaytlar ──────────────────
[[slides.items]]
icon = "pisilogo"
title = "Pisi GNU/Linux'a Hoş Geldiniz"
description = "Pisi; hız, kararlılık ve kullanım kolaylığını bir araya getiren Türkçe destekli bir GNU/Linux dağıtımıdır."
[[slides.items]]
icon = "pisi"
title = "PISI Paket Yöneticisi"
description = "Binlerce pakete tek komutla erişin. `pisi install <paket>` ile ihtiyacınız olan her şeyi kolayca kurabilirsiniz."
[[slides.items]]
icon = "community"
title = "Topluluk Desteği"
description = "forum.pisilinux.org adresindeki aktif topluluğumuz sorularınızı yanıtlamak için hazır. Wiki sayfalarımız da her an erişilebilir."
[[slides.items]]
icon = "pisi_speed"
title = "Hız ve Performans"
description = "Optimize edilmiş çekirdek ve başlangıç servisleri sayesinde PisiLinux saniyeler içinde açılır ve minimum kaynak kullanır."
[[slides.items]]
icon = "securety"
title = "Güvenlik Önce Gelir"
description = "Düzenli güvenlik güncellemeleri, AppArmor profilleri ve güvenli varsayılan ayarlarla sisteminizi koruyun."
[[slides.items]]
icon = "customization"
title = "Özelleştirme"
description = "Masaüstü ortamından tema renklerine, panel düzeninden klavye kısayollarına kadar her şeyi zevkinize göre ayarlayın."
# ── XFCE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "xfce"
icon = "xfce"
title = "PisiLinux XFCE"
description = "Hafif, hızlı ve kullanıcı dostu XFCE masaüstü ortamı. Kaynak tüketimi düşük, performansı yüksektir."
[[slides.items]]
desktop = "xfce"
icon = "xfce_panel"
title = "XFCE Panel"
description = "Geleneksel panel düzeni ile görev çubuğu, uygulama menüsü ve sistem tepsisi her zaman elinizin altında."
[[slides.items]]
desktop = "xfce"
icon = "file_manager"
title = "Thunar Dosya Yöneticisi"
description = "Hızlı ve hafif Thunar ile dosya işlemlerinizi kolayca yapın. Ağaç görünümü, sekmeli gezinme ve özelleştirilebilir özellikler sunar."
[[slides.items]]
desktop = "xfce"
icon = "xfce_settings"
title = "Ayarlar Merkezi"
description = "Klasik uygulama menüsüne modern bir alternatif. Whisker Menu ile uygulamaları kolayca bulun, favorilerinizi düzenleyin."
# ── KDE Plasma ────────────────────────────────────────────────────
[[slides.items]]
desktop = "kde"
icon = "kde"
title = "PisiLinux KDE Plasma"
description = "Modern, özelleştirilebilir ve güçlü KDE Plasma masaüstü ortamıyla tanışın. Her şey elinizin altında."
[[slides.items]]
desktop = "kde"
icon = "file_manager"
title = "Dolphin Dosya Yöneticisi"
description = "Sekmeli gezinme, ikili panel ve güçlü arama özelliğiyle Dolphin dosyalarınızı yönetmeyi kolaylaştırır."
[[slides.items]]
desktop = "kde"
icon = "kde_connect"
title = "KDE Bağlan"
description = "Telefonunuzu bilgisayarınıza bağlayın. Bildirimler, dosya aktarımı ve klipboard paylaşımı KDE Connect ile mümkün."
[[slides.items]]
desktop = "kde"
icon = "discover"
title = "Discover Yazılım Merkezi"
description = "Discover ile uygulamaları görsel arayüzden kolayca bulun, kurun ve güncelleyin."
# ── GNOME ─────────────────────────────────────────────────────────
[[slides.items]]
desktop = "gnome"
icon = "gnome"
title = "PisiLinux GNOME"
description = "Sade ve odaklı GNOME masaüstü ortamı ile dikkat dağıtıcı unsurları azaltın, işinize konsantre olun."
[[slides.items]]
desktop = "gnome"
icon = "gnome_search"
title = "GNOME Arama"
description = "Super tuşuna basıp yazmaya başlayın. Uygulama, dosya ve ayarlara anında ulaşın."
[[slides.items]]
desktop = "gnome"
icon = "gnome_extensions"
title = "GNOME Eklentileri"
description = "extensions.gnome.org adresinden yüzlerce eklentiyle masaüstünüzü ihtiyaçlarınıza göre şekillendirin."
# ── MATE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "mate"
icon = "mate"
title = "PisiLinux MATE"
description = "Geleneksel masaüstü düzenini sevenler için MATE. GTK+ 3 ile geliştirilmiş, modern ve akıcı bir deneyim sunar."
[[slides.items]]
desktop = "mate"
icon = "file_manager"
title = "Caja Dosya Yöneticisi"
description = "MATE'in güçlü dosya yöneticisi Caja ile dosya işlemlerinizi kolayca yapın. Çift panel, sekmeli gezinme ve geniş özellikler sunar."
[[slides.items]]
desktop = "mate"
icon = "mate_settings"
title = "MATE Ayarları"
description = "Görünüm, ekran, klavye ve daha fazlası için kapsamlı ayarlarla MATE masaüstünüzü özelleştirin."
[[slides.items]]
desktop = "mate"
icon = "customization"
title = "Zengin Uygulama Paketi"
description = "MATE; metin editöründen görüntü görüntüleme yazılımına kadar geniş, iyi entegre edilmiş klasik uygulamalar sunar."
# ── Budgie ────────────────────────────────────────────────────────
[[slides.items]]
desktop = "budgie"
icon = "budgie"
title = "PisiLinux Budgie"
description = "Modern, sade ve şık Budgie masaüstü. GNOME altyapısı üzerine inşa edilmiş, kullanıcı dostudur."
[[slides.items]]
desktop = "budgie"
icon = "budgie_applets"
title = "Budgie Desktop Applets"
description = "Budgie'nin panel appletleri sayesinde hava durumu, sistem izleyici, takvim gibi araçları kolayca ekleyin."
[[slides.items]]
desktop = "budgie"
icon = "budgie_raven"
title = "Raven Bildirim Merkezi"
description = "Raven panelinden ses seviyesi, medya kontrolleri, bildirimler ve hızlı ayarlara erişin."
[[slides.items]]
desktop = "budgie"
icon = "budgie_gtk_theme"
title = "GTK Tema Desteği"
description = "Budgie, GTK temalarını ve GNOME uygulamalarını sorunsuz çalıştırır, görsel bütünlük sağlar."
# ── Cinnamon ──────────────────────────────────────────────────────
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon"
title = "PisiLinux Cinnamon"
description = "Geleneksel masaüstü deneyimini modern özelliklerle birleştiren Cinnamon, güçlü ve esnek bir ortam sunar."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_panel"
title = "Cinnamon Paneli"
description = "Panelinizi applet'ler, başlatıcılar ve sistem göstergeleriyle özelleştirin; klasik ama güçlü bir masaüstü deneyimi yaşayın."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_settings"
title = "Ayarlar Yöneticisi"
description = "Cinnamon'un kapsamlı Ayarlar Yöneticisi aracılığıyla masaüstünüzün her yönünü özelleştirin."
[[slides.items]]
desktop = "cinnamon"
icon = "cinnamon_desklets"
title = "Cinnamon Desklets"
description = "Masaüstü widget'ları ile notlar, saatler ve sistem bilgileri her zaman görünür kalsın."
# ── LXQt ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lxqt"
icon = "lxqt"
title = "PisiLinux LXQt"
description = "Hafif, hızlı ve modern LXQt masaüstü ortamı. Kaynak tüketimi düşük, performansı yüksektir."
[[slides.items]]
desktop = "lxqt"
icon = "file_manager"
title = "LXQt Panel"
description = "Geleneksel panel düzeni ile görev çubuğu, uygulama başlatıcı ve sistem tepsisi her zaman elinizin altında."
[[slides.items]]
desktop = "lxqt"
icon = "file_manager"
title = "PCManFM-Qt Dosya Yöneticisi"
description = "Hafif ve hızlı PCManFM-Qt ile dosya işlemlerini kolayca yönetin. Sekmeli gezinme ve özelleştirilebilir arayüz sunar."
# ── LXDE ──────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lxde"
icon = "lxde"
title = "PisiLinux LXDE"
description = "Efsanevi hafif masaüstü LXDE. Çok düşük kaynak tüketimi ve güvenilir yapısıyla tanınır."
[[slides.items]]
desktop = "lxde"
icon = "lxde"
title = "Openbox Tabanlı"
description = "LXDE, Openbox pencere yöneticisini kullanır. İnce ayarlar ile akıcı bir deneyim sunar."
[[slides.items]]
desktop = "lxde"
icon = "file_manager"
title = "PCManFM Dosya Yöneticisi"
description = "PCManFM, hızlı ve hafif yapısıyla dosya işlemlerinizi kolaylaştırır, ağaç görünümü ve sekme destekler."
# ── Lumina ────────────────────────────────────────────────────────
[[slides.items]]
desktop = "lumina"
icon = "lumina"
title = "PisiLinux Lumina"
description = "Hafif ve bağımsız Lumina masa üstü. Qt tabanlı, BSD ve Linux sistemlerde düzgün çalışan minimal bir ortam."
[[slides.items]]
desktop = "lumina"
icon = "customization"
title = "Tema Özelleştirme"
description = "Lumina'nın tema sistemiyle renk şemaları ve arayüz stilini kolayca özelleştirin."
# ──────────────────────────────────────────────────────────────────
# [theme] Arayüz renk şeması (Hex)
# ──────────────────────────────────────────────────────────────────
[theme]
primary = "#1a73e8"
accent = "#34a853"
background = "#181824"
text = "#e2e8f0"
# ──────────────────────────────────────────────────────────────────
# [oem] Üretici (OEM) Kurulum Modu
# ──────────────────────────────────────────────────────────────────
[oem]
enabled = false
skip_steps = []
default_username = "oem"
default_hostname = "pisi-oem"
default_password = "oempisi-changeme"
+295 -104
View File
@@ -8,8 +8,11 @@ confirm = "Confirm"
# ── Welcome ───────────────────────────────────────────────
welcome = "Welcome"
welcome_title = "Welcome to YALI Installation Tool"
welcome_description = "This tool will guide you through the installation process."
welcome_description = "This tool will guide you through the installation process. Information about the operating system to be installed on your system:"
select_language = "Select Language:"
os = "OS"
desktop = "Desktop"
version = "Version"
# ── Location ──────────────────────────────────────────────
location = "Location"
@@ -72,108 +75,6 @@ execution_description = "Please wait. This may take a few minutes."
start_install = "Start Installation"
install_starting = "Starting installation…"
# ── Slides for PISILINUX ────────────────────────────────────────────
welcome_slide_title = "Welcome to Pisi GNU/Linux"
welcome_slide_description = "Pisi is a Turkish-supported GNU/Linux distribution that combines speed, stability, and ease of use."
package_manager_title = "PISI Package Manager"
package_manager_description = "Access thousands of packages with a single command. Install anything you need easily with `pisi install <package>`."
community_title = "Community Support"
community_description = "Our active community at forum.pisilinux.org is ready to answer your questions. Our wiki pages are always accessible."
speed_title = "Speed & Performance"
speed_description = "Thanks to an optimized kernel and startup services, PisiLinux boots in seconds and uses minimal resources."
security_title = "Security First"
security_description = "Keep your system secure with regular security updates, AppArmor profiles, and secure default settings."
customization_title = "Customization"
customization_description = "Tailor everything to your taste — from the desktop environment and theme colors to panel layout and keyboard shortcuts."
# ── Slides for BUDGIE ────────────────────────────────────────────
budgie_title = "PisiLinux Budgie"
budgie_description = "A modern, clean, and elegant Budgie desktop. Built on the GNOME stack and user-friendly."
budgie_applets_title = "Budgie Desktop Applets"
budgie_applets_description = "Easily add tools like weather, system monitor, and calendar via Budgie's panel applets."
budgie_raven_title = "Raven Notification Center"
budgie_raven_description = "Access volume, media controls, notifications, and quick settings from the Raven panel."
budgie_gtk_theme_title = "GTK Theme Support"
budgie_gtk_theme_description = "Budgie runs GTK themes and GNOME applications seamlessly, ensuring visual consistency."
# ── Slides for CINNAMON ────────────────────────────────────────────
cinnamon_title = "PisiLinux Cinnamon"
cinnamon_description = "Cinnamon combines the traditional desktop experience with modern features, offering a powerful and flexible environment."
cinnamon_panel_title = "Cinnamon Panel"
cinnamon_panel_description = "Customize your panel with applets, launchers, and system indicators for a classic yet powerful desktop."
cinnamon_settings_title = "Settings Manager"
cinnamon_settings_description = "Fine-tune every aspect of your Cinnamon desktop through the comprehensive Settings Manager."
cinnamon_desklets_title = "Cinnamon Desklets"
cinnamon_desklets_description = "Keep notes, clocks, and system info always visible on your desktop with Cinnamon Desklets."
# ── Slides for GNOME ────────────────────────────────────────────
gnome_title = "PisiLinux GNOME"
gnome_description = "Reduce distractions and focus on your work with the clean and focused GNOME desktop environment."
gnome_search_title = "GNOME Search"
gnome_search_description = "Press the Super key and start typing. Instantly reach applications, files, and settings."
gnome_extensions_title = "GNOME Extensions"
gnome_extensions_description = "Shape your desktop to your needs with hundreds of extensions from extensions.gnome.org."
# ── Slides for KDE/Plasma ────────────────────────────────────────────
kde_title = "PisiLinux KDE Plasma"
kde_description = "Meet the modern, customizable, and powerful KDE Plasma desktop. Everything is at your fingertips."
dolphin_title = "Dolphin File Manager"
dolphin_description = "Dolphin makes managing your files easy with tabbed browsing, dual-panel view, and powerful search."
kde_connect_title = "KDE Connect"
kde_connect_description = "Connect your phone to your computer. Notifications, file transfer, and clipboard sharing — all with KDE Connect."
discover_title = "Discover Software Center"
discover_description = "Easily find, install, and update applications from the visual interface with Discover."
# ── Slides for LXQt ────────────────────────────────────────────
lxqt_title = "PisiLinux LXQt"
lxqt_description = "A lightweight, fast, and modern LXQt desktop environment. Low resource consumption, high performance."
lxqt_panel_title = "LXQt Panel"
lxqt_panel_description = "The traditional panel layout puts the taskbar, application launcher, and system tray always within reach."
lxqt_pcmanfm_qt_title = "PCManFM-Qt File Manager"
lxqt_pcmanfm_qt_description = "Manage files effortlessly with the lightweight and fast PCManFM-Qt. Tabbed navigation and a customizable interface."
lxqt_speed_title = "LXQt Speed"
lxqt_speed_description = "LXQt is engineered for speed. Boot faster, respond quicker, and make the most of even older hardware."
# ── Slides for LXDE ────────────────────────────────────────────
lxde_title = "PisiLinux LXDE"
lxde_description = "The legendary lightweight LXDE desktop. Known for extremely low resource usage and a reliable build."
lxde_openbox_title = "Openbox-Based"
lxde_openbox_description = "LXDE uses the Openbox window manager. Fine-tune it for a smooth and responsive experience."
lxde_pcmanfm_title = "PCManFM File Manager"
lxde_pcmanfm_description = "PCManFM speeds up your file operations with its fast and lightweight build, supporting tree view and tabs."
lxde_lxpanel_title = "LXPanel"
lxde_lxpanel_description = "LXPanel provides a taskbar, application menu, and system tray in a classic desktop layout."
# ── Slides for LUMINA ────────────────────────────────────────────
lumina_title = "PisiLinux Lumina"
lumina_description = "A lightweight and independent Lumina desktop. Qt-based, designed to run cleanly on BSD and Linux systems."
lumina_applications_title = "Application Support"
lumina_applications_description = "Lumina works well with core utilities and many popular Qt apps, providing a practical everyday experience."
lumina_theme_title = "Theme Customization"
lumina_theme_description = "Easily customize color schemes and interface styles with Lumina's flexible theme system."
lumina_panel_title = "Lumina Panel"
lumina_panel_description = "A clean panel with a taskbar, application launcher, and system tray keeps your desktop organized."
# ── Slides for MATE ────────────────────────────────────────────
mate_title = "PisiLinux MATE"
mate_description = "MATE for those who love the traditional desktop layout. Developed with GTK+ 3 for a modern and smooth experience."
mate_caja_title = "Caja File Manager"
mate_caja_description = "Manage your files easily with MATE's powerful Caja. Dual panel, tabbed navigation, and rich features."
mate_settings_title = "MATE Settings"
mate_settings_description = "Customize your MATE desktop with comprehensive settings for appearance, display, keyboard, and more."
mate_applications_title = "Rich Application Suite"
mate_applications_description = "MATE comes with a rich set of classic applications — from text editors to image viewers, all well-integrated."
# ── Slides for XFCE ────────────────────────────────────────────
xfce_title = "PisiLinux XFCE"
xfce_description = "A lightweight, fast, and user-friendly XFCE desktop environment. Low resource consumption, high performance."
xfce_panel_title = "XFCE Panel"
xfce_panel_description = "The traditional panel layout puts the taskbar, application menu, and system tray always within reach."
xfce_thunar_title = "Thunar File Manager"
xfce_thunar_description = "Manage your files easily with the fast and lightweight Thunar. Tree view, tabbed navigation, and customizable features."
xfce_settings_title = "Settings Manager"
xfce_settings_description = "A modern alternative to the classic application menu. Find apps easily and organize your favorites with Whisker Menu."
# ── Finish ────────────────────────────────────────────────
finish = "Finished"
@@ -200,11 +101,34 @@ partition_table_type = "Partition table"
disk_too_small = "Disk too small"
summary_partition_plan = "Partitioning"
summary_custom_partitions = "Manual Partitions"
summary_bootloader = "Bootloader"
summary_datetime = "Date & Time"
summary_ntp_enabled = "NTP enabled (auto)"
summary_root = "Root"
summary_root_enabled = "Password set"
summary_root_disabled = "Disabled (locked)"
summary_software = "Software"
summary_display_manager = "Display Manager"
summary_autologin = "Autologin"
summary_autologin_user = "Autologin User"
summary_desktop = "Desktop"
# ── Display Manager ───────────────────────────────
display_manager = "Display Manager"
display_manager_title = "Display Manager Configuration"
display_manager_description = "Select your display manager and desktop environment."
display_manager_select = "Display manager:"
display_manager_de = "Desktop environment:"
display_manager_autologin = "Automatic login"
display_manager_autologin_user = "Autologin user:"
display_manager_autologin_hint = "(first user)"
display_manager_de_not_found = "(not detected)"
display_manager_note = "Note: The display manager will be enabled by default on the installed system."
mp_size_remaining = "Remaining Space"
# ── UI / General ──────────────────────────────────────────
installer = "YALI Installation Tool"
os_name = "PiSi GNU/LINUX"
app_name = "YALI"
# ── Error screen ──────────────────────────────────────────
error_show_log = "▼ Show Log Details"
@@ -227,6 +151,7 @@ pwd_weak = "Weak"
pwd_medium = "Medium"
pwd_strong = "Strong"
pwd_very_strong = "Very strong"
use_same_for_admin = "Use the same password for the administrator (root) account"
# ── Checker ───────────────────────────────────────────────
check_disk_space = "Disk Space"
@@ -256,8 +181,13 @@ job_fstab = "Generating fstab"
job_hostname = "Configuring hostname"
job_locale = "Configuring locale"
job_timezone = "Setting timezone"
job_datetime = "Configuring date & time"
job_keyboard = "Configuring keyboard layout"
job_user = "Creating user account"
job_install_packages = "Installing selected packages"
job_ldconfig = "Configuring shared libraries (ldconfig)"
job_update_env = "Updating system environment"
job_cleanup_live = "Cleaning up live environment traces"
# ── Manual Partition UI ────────────────────────────────────
manual_partition_title = "Manual Partitioning"
@@ -324,8 +254,269 @@ mp_delete_confirm_yes = "✓ Yes, Delete"
plan_root_suffix = " (root)"
disk_min_required = " ↳ %{err} (min. %{n} GB required)"
error_partition_plan_not_found = "Partition plan not found."
# ── License ───────────────────────────────────────────────
license = "License"
license_title = "GNU General Public License"
license_description = "Please read and accept the license terms to continue."
license_accept = "I have read and accept the terms of the GNU General Public License."
# ── Admin (root) ───────────────────────────────────────────
admin = "Admin"
admin_title = "Root Password"
admin_description = "Set a password for the root (administrator) account. Leave empty to disable direct root login."
admin_password_label = "Root password:"
admin_password_confirm_label = "Confirm root password:"
admin_password_short = "Root password must be at least 4 characters."
# ── Date/Time ──────────────────────────────────────────────
datetime = "Date & Time"
datetime_title = "Date & Time Settings"
datetime_description = "Configure the system date and time."
datetime_ntp_label = "Network Time (NTP):"
datetime_ntp_on = "Enabled — time syncs automatically"
datetime_ntp_off = "Disabled — set manually"
datetime_date_label = "Date:"
datetime_time_label = "Time:"
datetime_year = "Year:"
datetime_month = "Month:"
datetime_day = "Day:"
datetime_ntp_info = "NTP will synchronize the system clock over the network after installation."
datetime_manual_info = "Set the date and time manually. The system will use these values."
# ── Network ────────────────────────────────────────────────
network = "Network"
network_title = "Network Configuration"
network_description = "Configure network connections and proxy settings."
network_connected = "Connected:"
network_disconnected = "Disconnected"
network_scan = "🔍 Scan Wi-Fi Networks"
network_rescan = "🔄 Rescan"
network_scan_count = "Networks found:"
network_connect = "Connect"
network_password_hint = "Password"
network_proxy_label = "Configure proxy:"
# ── Software ───────────────────────────────────────────────
software = "Software"
software_title = "Software Selection"
software_description = "Choose which software to install."
software_desktops = "Desktop Environments"
software_desktop_xfce = "XFCE — Lightweight and fast"
software_desktop_kde = "KDE Plasma — Modern and feature-rich"
software_desktop_gnome = "GNOME — Clean and focused"
software_desktop_cinnamon = "Cinnamon — Traditional and powerful"
software_additional = "Additional Collections"
software_office = "Office — LibreOffice, document tools"
software_development = "Development — GCC, Git, IDE tools"
software_multimedia = "Multimedia — Codecs, players, editors"
software_minimal = "⚠ Minimal — only base system (overrides others)"
software_minimal_warning = "Selected 'Minimal' will override all other choices. Only essential packages will be installed."
# ── Bootloader ─────────────────────────────────────────────
bootloader = "Bootloader"
bootloader_title = "Bootloader Configuration"
bootloader_description = "Configure the bootloader (GRUB) settings."
bootloader_device_label = "Install to device:"
bootloader_device_hint = "(default: target disk)"
bootloader_timeout_label = "Timeout:"
bootloader_timeout_suffix = "seconds"
bootloader_password_label = "GRUB password (optional):"
bootloader_kernel_label = "Kernel parameters:"
bootloader_kernel_hint = "(e.g. quiet splash)"
rescue = "Rescue"
rescue_button = "🆘 Rescue Mode"
rescue_title = "Rescue Mode"
rescue_description = "Repair an existing PisiLinux installation."
rescue_no_installations = "No Linux partitions found. Please check the system and try again."
rescue_scan = "🔍 Scan"
rescue_detected = "Detected Linux Partitions:"
rescue_chroot = "📂 Rescue Shell (chroot)"
rescue_chroot_start = "Starting chroot shell…"
rescue_grub = "🔧 Reinstall GRUB"
rescue_grub_start = "Reinstalling GRUB…"
rescue_return = "↩ Return to Normal Installer"
rescue_not_available = "Rescue mode: no existing Linux installation found."
rescue_pisi_history = "📜 Pisi History Rollback"
rescue_pisi_history_title = "Pisi Package History"
rescue_pisi_history_desc = "Select a history entry and click Rollback to revert to that state."
rescue_pisi_history_scan = "🔍 List History"
rescue_pisi_history_empty = "No package history found or could not be read."
rescue_pisi_takeback = "↩ Rollback to This Point"
rescue_pisi_takeback_start = "Running pisi takeback…"
rescue_pisi_history_ok = "Rollback completed successfully."
rescue_pisi_history_err = "Error: %{err}"
# ── LUKS ──────────────────────────────────────────────────
luks_enable_root = "Encrypt root partition (LUKS)"
luks_password = "Encryption password:"
luks_password_weak = "Password is too short (minimum 4 characters)"
error_target_disk_not_selected = "No target disk selected."
error_thread_terminated = "Install thread terminated unexpectedly."
error_thread_unexpectedly_terminated = "Install thread terminated unexpectedly."
# ── Netinstall ─────────────────────────────────────────
netinstall = "Software Selection"
netinstall_title = "Extra Software"
netinstall_description = "Select additional package groups to install."
netinstall_package_count = "packages"
netinstall_selected = "selected"
# ── Media Check ──────────────────────────────────────────────
media_check = "Media Check"
media_check_title = "Verify Installation Media"
media_check_description = "Check the integrity of your ISO or USB drive."
media_check_scan = "🔍 Scan Media"
media_check_select = "Select file to verify:"
media_check_running = "Checking… please wait."
media_check_ok = "✅ Media integrity verified successfully."
media_check_fail = "❌ Media integrity check FAILED! Please re-download."
media_check_no_md5 = "⚠️ md5sum or sha256sum tools not found."
media_check_file_not_found = "⚠️ Selected file not found."
media_check_path_label = "File path:"
media_check_path_hint = "e.g. /run/live/medium/pisilinux.iso"
media_check_algorithm = "Algorithm:"
job_media_check = "Verifying installation media"
# ── CLI / Auto-install ─────────────────────────────────────
hostname = "Hostname"
timezone = "Timezone"
username = "Username"
# ── Slideshow (fallback) ───────────────────────────────────
welcome_slide_title = "Welcome to Pisi Linux"
welcome_slide_description = "A modern, fast, and secure GNU/Linux distribution."
package_manager_title = "Pisi Package Manager"
package_manager_description = "A powerful package manager with dependency resolution, history rollback, and atomic updates."
community_title = "Community Driven"
community_description = "Built by the community for the community. Pisi Linux is free and open source."
speed_title = "Optimized for Speed"
speed_description = "Designed to run fast on both modern and older hardware."
security_title = "Security First"
security_description = "Regular security updates and a robust permission system keep your system safe."
customization_title = "Full Customization"
customization_description = "Choose your desktop, applications, and look & feel during installation."
budgie_title = "Budgie Desktop"
budgie_description = "A modern, elegant desktop environment designed with simplicity in mind."
budgie_applets_title = "Budgie Applets"
budgie_applets_description = "Customizable applets for notifications, system tray, and more."
budgie_raven_title = "Budgie Raven"
budgie_raven_description = "Notification center and settings panel for quick access."
budgie_gtk_theme_title = "GTK Theme Integration"
budgie_gtk_theme_description = "Seamless GTK theme support for consistent application appearance."
cinnamon_title = "Cinnamon Desktop"
cinnamon_description = "A traditional desktop layout with modern features."
cinnamon_panel_title = "Cinnamon Panel"
cinnamon_panel_description = "Flexible panel system with applets, launchers, and tray icons."
cinnamon_settings_title = "Cinnamon Settings"
cinnamon_settings_description = "Comprehensive system settings accessible from the menu."
cinnamon_desklets_title = "Cinnamon Desklets"
cinnamon_desklets_description = "Interactive desklets for weather, clock, system monitoring and more."
gnome_title = "GNOME Desktop"
gnome_description = "A clean, distraction-free desktop environment."
gnome_search_title = "GNOME Search"
gnome_search_description = "Instant search across applications, files, and settings."
gnome_extensions_title = "GNOME Extensions"
gnome_extensions_description = "Extend your desktop with community-built extensions."
kde_title = "KDE Plasma Desktop"
kde_description = "Feature-rich, highly customizable desktop environment."
dolphin_title = "Dolphin File Manager"
dolphin_description = "Intuitive file manager with split-pane view and powerful search."
kde_connect_title = "KDE Connect"
kde_connect_description = "Seamlessly integrate your phone with your desktop."
discover_title = "Discover Software Center"
discover_description = "Browse and install applications with a single click."
lxde_title = "LXDE Desktop"
lxde_description = "Lightweight desktop environment for older hardware."
lxde_openbox_title = "OpenBox Window Manager"
lxde_openbox_description = "Fast, lightweight window manager with keyboard-driven workflow."
lxde_pcmanfm_title = "PCManFM File Manager"
lxde_pcmanfm_description = "Lightweight file manager with tabbed browsing and desktop management."
lxde_lxpanel_title = "LXPanel"
lxde_lxpanel_description = "Lightweight panel with application menu, task bar, and system tray."
lxqt_title = "LXQt Desktop"
lxqt_description = "A lightweight Qt-based desktop environment."
lxqt_panel_title = "LXQt Panel"
lxqt_panel_description = "Highly configurable panel with widgets and launchers."
lxqt_pcmanfm_qt_title = "PCManFM-Qt File Manager"
lxqt_pcmanfm_qt_description = "Fast file manager with tabbed interface and desktop icons."
lumina_title = "Lumina Desktop"
lumina_description = "A lightweight, plugin-based desktop environment from TrueOS."
lumina_applications_title = "Lumina Applications"
lumina_applications_description = "Native applications for Lumina desktop environment."
lumina_theme_title = "Lumina Theme Engine"
lumina_theme_description = "Full theme support for a consistent visual experience."
lumina_panel_title = "Lumina Panel"
lumina_panel_description = "Flexible panel system with custom widgets."
mate_title = "MATE Desktop"
mate_description = "Traditional desktop environment continued from GNOME 2."
mate_caja_title = "Caja File Manager"
mate_caja_description = "Feature-rich file manager with spatial and browser views."
mate_settings_title = "MATE Settings"
mate_settings_description = "Complete system settings organized by category."
mate_applications_title = "MATE Applications"
mate_applications_description = "Integrated application suite for daily tasks."
xfce_title = "XFCE Desktop"
xfce_description = "Lightweight yet powerful desktop environment."
xfce_panel_title = "XFCE Panel"
xfce_panel_description = "Highly customizable panel with launchers, applets, and menus."
xfce_thunar_title = "Thunar File Manager"
xfce_thunar_description = "Fast and reliable file manager with bulk rename and plugins."
xfce_settings_title = "XFCE Settings"
xfce_settings_description = "Comprehensive settings manager for desktop customization."
# ── LVM ────────────────────────────────────────────────────
lvm_lv_title = "LVM Logical Volumes"
lvm_lv_name = "LV Name"
lvm_lv_vg = "Volume Group"
lvm_lv_add = "Add Logical Volume"
lvm_lv_error_vg = "At least one Volume Group must be defined."
lvm_lv_error_size = "Entered size exceeds Volume Group capacity."
lvm_lv_mountpoint = "Mount Point"
lvm_lv_fstype = "File System"
lvm_lv_size = "Size (MB, 0=remaining):"
lvm_lv_action = "Action"
+305 -107
View File
@@ -8,8 +8,11 @@ confirm = "Onayla"
# ── Welcome ───────────────────────────────────────────────
welcome = "Hoş Geldiniz"
welcome_title = "YALI Kurulum Aracına Hoş Geldiniz"
welcome_description = "Bu araç size kurulum sürecinde rehberlik edecektir."
welcome_description = "Bu araç size kurulum sürecinde rehberlik edecektir. Sisteminize kurulacak olan İşletim Sistemi bilgileri:"
select_language = "Dil Seçiniz:"
os = "İşletim Sistemi"
desktop = "Masaüstü"
version = "Sürüm"
# ── Location ──────────────────────────────────────────────
location = "Konum"
@@ -72,110 +75,6 @@ execution_description = "Lütfen bekleyin. Bu işlem birkaç dakika sürebilir."
start_install = "Kurulumu Başlat"
install_starting = "Kurulum başlatılıyor…"
# ── Slides for PISILINUX ────────────────────────────────────────────
welcome_slide_title = "Pisi GNU/Linux'a Hoş Geldiniz"
welcome_slide_description = "Pisi; hız, kararlılık ve kullanım kolaylığını bir araya getiren Türkçe destekli bir GNU/Linux dağıtımıdır."
package_manager_title = "PISI Paket Yöneticisi"
package_manager_description = "Binlerce pakete tek komutla erişin. `pisi install <paket>` ile ihtiyacınız olan her şeyi kolayca kurabilirsiniz."
community_title = "Topluluk Desteği"
community_description = "forum.pisilinux.org adresindeki aktif topluluğumuz sorularınızı yanıtlamak için hazır. Wiki sayfalarımız da her an erişilebilir."
speed_title = "Hız ve Performans"
speed_description = "Optimize edilmiş çekirdek ve başlangıç servisleri sayesinde PisiLinux saniyeler içinde açılır ve minimum kaynak kullanır."
security_title = "Güvenlik Önce Gelir"
security_description = "Düzenli güvenlik güncellemeleri, AppArmor profilleri ve güvenli varsayılan ayarlarla sisteminizi koruyun."
customization_title = "Özelleştirme"
customization_description = "Masaüstü ortamından tema renklerine, panel düzeninden klavye kısayollarına kadar her şeyi zevkinize göre ayarlayın."
# ── Slides for BUDGIE ────────────────────────────────────────────
budgie_title = "PisiLinux Budgie"
budgie_description = "Modern, sade ve şık Budgie masaüstü. GNOME altyapısı üzerine inşa edilmiş, kullanıcı dostudur."
budgie_applets_title = "Budgie Desktop Applets"
budgie_applets_description = "Budgie'nin panel appletleri sayesinde hava durumu, sistem izleyici, takvim gibi araçları kolayca ekleyin."
budgie_raven_title = "Raven Bildirim Merkezi"
budgie_raven_description = "Raven panelinden ses seviyesi, medya kontrolleri, bildirimler ve hızlı ayarlara erişin."
budgie_gtk_theme_title = "GTK Tema Desteği"
budgie_gtk_theme_description = "Budgie, GTK temalarını ve GNOME uygulamalarını sorunsuz çalıştırır, görsel bütünlük sağlar."
# ── Slides for CINNAMON ────────────────────────────────────────────
cinnamon_title = "PisiLinux Cinnamon"
cinnamon_description = "Geleneksel masaüstü deneyimini modern özelliklerle birleştiren Cinnamon, güçlü ve esnek bir ortam sunar."
cinnamon_panel_title = "Cinnamon Paneli"
cinnamon_panel_description = "Panelinizi applet'ler, başlatıcılar ve sistem göstergeleriyle özelleştirin; klasik ama güçlü bir masaüstü deneyimi yaşayın."
cinnamon_settings_title = "Ayarlar Yöneticisi"
cinnamon_settings_description = "Cinnamon'un kapsamlı Ayarlar Yöneticisi aracılığıyla masaüstünüzün her yönünü özelleştirin."
cinnamon_desklets_title = "Cinnamon Desklets"
cinnamon_desklets_description = "Masaüstü widget'ları ile notlar, saatler ve sistem bilgileri her zaman görünür kalsın."
# ── Slides for GNOME ────────────────────────────────────────────
gnome_title = "PisiLinux GNOME"
gnome_description = "Sade ve odaklı GNOME masaüstü ortamı ile dikkat dağıtıcı unsurları azaltın, işinize konsantre olun."
gnome_search_title = "GNOME Arama"
gnome_search_description = "Super tuşuna basıp yazmaya başlayın. Uygulama, dosya ve ayarlara anında ulaşın."
gnome_extensions_title = "GNOME Eklentileri"
gnome_extensions_description = "extensions.gnome.org adresinden yüzlerce eklentiyle masaüstünüzü ihtiyaçlarınıza göre şekillendirin."
# ── Slides for KDE/Plasma ────────────────────────────────────────────
kde_title = "PisiLinux KDE Plasma"
kde_description = "Modern, özelleştirilebilir ve güçlü KDE Plasma masaüstü ortamıyla tanışın. Her şey elinizin altında."
dolphin_title = "Dolphin Dosya Yöneticisi"
dolphin_description = "Sekmeli gezinme, ikili panel ve güçlü arama özelliğiyle Dolphin dosyalarınızı yönetmeyi kolaylaştırır."
kde_connect_title = "KDE Bağlan"
kde_connect_description = "Telefonunuzu bilgisayarınıza bağlayın. Bildirimler, dosya aktarımı ve klipboard paylaşımı KDE Connect ile mümkün."
discover_title = "Discover Yazılım Merkezi"
discover_description = "Discover ile uygulamaları görsel arayüzden kolayca bulun, kurun ve güncelleyin."
# ── Slides for LXQt ────────────────────────────────────────────
lxqt_title = "PisiLinux LXQt"
lxqt_description = "Hafif, hızlı ve modern LXQt masaüstü ortamı. Kaynak tüketimi düşük, performansı yüksektir."
lxqt_panel_title = "LXQt Panel"
lxqt_panel_description = "Geleneksel panel düzeni ile görev çubuğu, uygulama başlatıcı ve sistem tepsisi her zaman elinizin altında."
lxqt_pcmanfm_qt_title = "PCManFM-Qt Dosya Yöneticisi"
lxqt_pcmanfm_qt_description = "Hafif ve hızlı PCManFM-Qt ile dosya işlemlerini kolayca yönetin. Sekmeli gezinme ve özelleştirilebilir arayüz sunar."
lxqt_speed_title = "LXQt Hızı"
lxqt_speed_description = "LXQt, hız için tasarlanmıştır. Daha hızlı açılır, daha çabuk yanıt verir ve eski donanımları en iyi şekilde kullanır."
# ── Slides for LXDE ────────────────────────────────────────────
lxde_title = "PisiLinux LXDE"
lxde_description = "Efsanevi hafif masaüstü LXDE. Çok düşük kaynak tüketimi ve güvenilir yapısıyla tanınır."
lxde_openbox_title = "Openbox Tabanlı"
lxde_openbox_description = "LXDE, Openbox pencere yöneticisini kullanır. İnce ayarlar ile akıcı bir deneyim sunar."
lxde_pcmanfm_title = "PCManFM Dosya Yöneticisi"
lxde_pcmanfm_description = "PCManFM, hızlı ve hafif yapısıyla dosya işlemlerinizi kolaylaştırır, ağaç görünümü ve sekme destekler."
lxde_lxpanel_title = "LXPanel"
lxde_lxpanel_description = "LXPanel, geleneksel masaüstü düzeni ile görev çubuğu, uygulama menüsü ve sistem tepsisi gibi temel özellikleri sunar."
# ── Slides for LUMINA ────────────────────────────────────────────
lumina_title = "PisiLinux Lumina"
lumina_description = "Hafif ve bağımsız Lumina masa üstü. Qt tabanlı, BSD ve Linux sistemlerde düzgün çalışan minimal bir ortam."
lumina_applications_title = "Uygulama Desteği"
lumina_applications_description = "Lumina; temel uygulamalar ve birçok popüler Qt uygulamasıyla uyumlu çalışır, kullanışlı bir deneyim sunar."
lumina_theme_title = "Tema Özelleştirme"
lumina_theme_description = "Lumina'nın tema sistemiyle renk şemaları ve arayüz stilini kolayca özelleştirin."
lumina_panel_title = "Lumina Paneli"
lumina_panel_description = "Görev çubuğu, uygulama başlatıcı ve sistem tepsisini içeren sade panel, masa üstünüzü düzenli tutar."
# ── Slides for MATE ────────────────────────────────────────────
mate_title = "PisiLinux MATE"
mate_description = "Geleneksel masaüstü düzenini sevenler için MATE. GTK+ 3 ile geliştirilmiş, modern ve akıcı bir deneyim sunar."
mate_caja_title = "Caja Dosya Yöneticisi"
mate_caja_description = "MATE'in güçlü dosya yöneticisi Caja ile dosya işlemlerinizi kolayca yapın. Çift panel, sekmeli gezinme ve geniş özellikler sunar."
mate_settings_title = "MATE Ayarları"
mate_settings_description = "Görünüm, ekran, klavye ve daha fazlası için kapsamlı ayarlarla MATE masaüstünüzü özelleştirin."
mate_applications_title = "Zengin Uygulama Paketi"
mate_applications_description = "MATE; metin editöründen görüntü görüntüleme yazılımına kadar geniş, iyi entegre edilmiş klasik uygulamalar sunar."
# ── Slides for XFCE ────────────────────────────────────────────
xfce_title = "PisiLinux XFCE"
xfce_description = "Hafif, hızlı ve kullanıcı dostu XFCE masaüstü ortamı. Kaynak tüketimi düşük, performansı yüksektir."
xfce_panel_title = "XFCE Panel"
xfce_panel_description = "Geleneksel panel düzeni ile görev çubuğu, uygulama menüsü ve sistem tepsisi her zaman elinizin altında."
xfce_thunar_title = "Thunar Dosya Yöneticisi"
xfce_thunar_description = "Hızlı ve hafif Thunar ile dosya işlemlerinizi kolayca yapın. Ağaç görünümü, sekmeli gezinme ve özelleştirilebilir özellikler sunar."
xfce_settings_title = "Ayarlar Merkezi"
xfce_settings_description = "Klasik uygulama menüsüne modern bir alternatif. Whisker Menu ile uygulamaları kolayca bulun, favorilerinizi düzenleyin."
# ── Finish ────────────────────────────────────────────────
finish = "Tamamlandı"
finish_title = "Kurulum Tamamlandı"
@@ -205,7 +104,7 @@ mp_size_remaining = "Kalan Alan"
# ── UI / Genel ────────────────────────────────────────────
installer = "YALI Kurulum Aracı"
os_name = "PiSi GNU/LİNUX"
app_name = "YALI"
# ── Hata ekranı ───────────────────────────────────────────
error_show_log = "▼ Log Detaylarını Göster"
@@ -221,6 +120,8 @@ check_fail_warning = "Bir veya daha fazla kritik gereksinim karşılanmıyor. L
check_warn_notice = "Bazı önerilen gereksinimler karşılanmıyor. Kurulum devam edebilir."
check_all_pass = "Tüm sistem gereksinimleri karşılandı."
recheck = "🔄 Yeniden Kontrol Et"
rescue_button = "🆘 Kurtarma Kipi"
rescue_button_tooltip = "PiSi Linux'u Kurtarma Modunda Yeniden Kur"
# ── Users (ek) ────────────────────────────────────────────
pwd_very_weak = "Çok zayıf"
@@ -237,7 +138,7 @@ check_disk_pass = "%{size} GB disk mevcut."
check_internet = "İnternet"
check_net_pass = "İnternet bağlantısı mevcut."
check_net_warn = "İnternet bağlantısı bulunamadı. Çevrimdışı kurulum devam edebilir, ancak güncellemeler ve ek paketler indirilemeyecek."
check_boot_mode = "Önyükleme Modu"
check_boot_mode = "Önyükleme Kipi"
check_uefi_pass = "UEFI modu tespit edildi. GPT kullanılacak."
check_bios_pass = "BIOS/Legacy modu tespit edildi. MBR kullanılacak."
check_live_sys = "Canlı Sistem"
@@ -329,3 +230,300 @@ error_partition_plan_not_found = "Bölümleme planı bulunamadı."
error_target_disk_not_selected = "Hedef disk seçilmedi."
error_thread_terminated = "İş parçacığı beklenmedik şekilde sonlandı."
error_thread_unexpectedly_terminated = "İş parçacığı beklenmedik şekilde sonlandı."
# ── Summary (ek) ─────────────────────────────────────────────
summary_bootloader = "Önyükleyici"
summary_datetime = "Tarih & Saat"
summary_ntp_enabled = "NTP etkin (otomatik)"
summary_root = "Kök (root)"
summary_root_enabled = "Parola belirlendi"
summary_root_disabled = "Devre dışı (kilitli)"
summary_software = "Yazılım"
summary_display_manager = "Görüntü Yöneticisi"
summary_autologin = "Otomatik oturum açma"
summary_autologin_user = "Otomatik oturum açılacak kullanıcı"
summary_desktop = "Masaüstü"
# ── Görüntü Yöneticisi ───────────────────────────────────────
display_manager = "Görüntü Yöneticisi"
display_manager_title = "Görüntü Yöneticisi Yapılandırması"
display_manager_description = "Görüntü yöneticinizi ve masaüstü ortamınızı seçin."
display_manager_select = "Görüntü yöneticisi:"
display_manager_de = "Masaüstü ortamı:"
display_manager_autologin = "Otomatik oturum açma"
display_manager_autologin_user = "Otomatik oturum açılacak kullanıcı:"
display_manager_autologin_hint = "(ilk kullanıcı)"
display_manager_de_not_found = "(algılanamadı)"
display_manager_note = "Not: Görüntü yöneticisi, kurulan sistemde varsayılan olarak etkin olacaktır."
# ── Kullanıcı (ek) ──────────────────────────────────────────────
use_same_for_admin = "Yönetici (root) hesabı için aynı parolayı kullan"
# ── Jobs (ek) ──────────────────────────────────────────────────
job_install_packages = "Seçilen paketler kuruluyor"
job_ldconfig = "Paylaşımlı kütüphaneler yapılandırılıyor (ldconfig)"
job_update_env = "Sistem ortamı güncelleniyor"
job_cleanup_live = "Canlı ortam izleri temizleniyor"
job_datetime = "Tarih & saat yapılandırılıyor"
job_display_manager = "Görüntü yöneticisi yapılandırılıyor"
job_netinstall = "Ek paketler kuruluyor"
job_setup_lvm = "LVM yapılandırılıyor"
# ── Lisans ───────────────────────────────────────────────────
license = "Lisans"
license_title = "GNU Genel Kamu Lisansı"
license_description = "Devam etmek için lisans koşullarını okuyun ve kabul edin."
license_accept = "GNU Genel Kamu Lisansı koşullarını okudum ve kabul ediyorum."
# ── Yönetici (root) ─────────────────────────────────────────
admin = "Yönetici"
admin_title = "Root Parolası"
admin_description = "Root (yönetici) hesabı için bir parola belirleyin. Boş bırakılırsa doğrudan root girişi devre dışı kalır."
admin_password_label = "Root parolası:"
admin_password_confirm_label = "Root parolası (tekrar):"
admin_password_short = "Root parolası en az 4 karakter olmalıdır."
# ── Tarih & Saat ────────────────────────────────────────────
datetime = "Tarih & Saat"
datetime_title = "Tarih & Saat Ayarları"
datetime_description = "Sistem tarihini ve saatini yapılandırın."
datetime_ntp_label = "Ağ Zamanı (NTP):"
datetime_ntp_on = "Etkin — saat otomatik eşitlenir"
datetime_ntp_off = "Devre dışı — elle ayarlanır"
datetime_date_label = "Tarih:"
datetime_time_label = "Saat:"
datetime_year = "Yıl:"
datetime_month = "Ay:"
datetime_day = "Gün:"
datetime_ntp_info = "NTP, kurulumdan sonra sistem saatini ağ üzerinden eşitleyecektir."
datetime_manual_info = "Tarih ve saati elle ayarlayın. Sistem bu değerleri kullanacaktır."
# ── Ağ ──────────────────────────────────────────────────────
network = "Ağ"
network_title = "Ağ Yapılandırması"
network_description = "Ağ bağlantılarını ve vekil sunucu (proxy) ayarlarını yapılandırın."
network_connected = "Bağlı:"
network_disconnected = "Bağlı değil"
network_scan = "🔍 Wi-Fi Ağlarını Tara"
network_rescan = "🔄 Yeniden Tara"
network_scan_count = "Bulunan ağlar:"
network_connect = "Bağlan"
network_password_hint = "Parola"
network_proxy_label = "Vekil sunucu (proxy) yapılandır:"
# ── Yazılım ─────────────────────────────────────────────────
software = "Yazılım"
software_title = "Yazılım Seçimi"
software_description = "Hangi yazılımların kurulacağını seçin."
software_desktops = "Masaüstü Ortamları"
software_desktop_xfce = "XFCE — Hafif ve hızlı"
software_desktop_kde = "KDE Plasma — Modern ve özellikli"
software_desktop_gnome = "GNOME — Sade ve odaklı"
software_desktop_cinnamon = "Cinnamon — Geleneksel ve güçlü"
software_additional = "Ek Koleksiyonlar"
software_office = "Ofis — LibreOffice, belge araçları"
software_development = "Geliştirme — GCC, Git, IDE araçları"
software_multimedia = "Multimedya — Kodekler, oynatıcılar, düzenleyiciler"
software_minimal = "⚠ Minimal — yalnızca temel sistem (diğerlerini geçersiz kılar)"
software_minimal_warning = "'Minimal' seçimi diğer tüm seçimleri geçersiz kılar. Yalnızca temel paketler kurulur."
# ── Önyükleyici ─────────────────────────────────────────────
bootloader = "Önyükleyici"
bootloader_title = "Önyükleyici Yapılandırması"
bootloader_description = "Önyükleyici (GRUB) ayarlarını yapılandırın."
bootloader_device_label = "Aygıta kur:"
bootloader_device_hint = "(varsayılan: hedef disk)"
bootloader_timeout_label = "Bekleme süresi:"
bootloader_timeout_suffix = "saniye"
bootloader_password_label = "GRUB parolası (isteğe bağlı):"
bootloader_kernel_label = "Çekirdek parametreleri:"
bootloader_kernel_hint = "(örn. quiet splash)"
# ── Kurtarma ────────────────────────────────────────────────
rescue = "Kurtarma"
rescue_title = "Kurtarma Modu"
rescue_description = "Mevcut bir PisiLinux kurulumunu onarın."
rescue_no_installations = "Linux bölümü bulunamadı. Lütfen sistemi kontrol edip tekrar deneyin."
rescue_scan = "🔍 Tara"
rescue_detected = "Algılanan Linux Bölümleri:"
rescue_chroot = "📂 Kurtarma Kabuğu (chroot)"
rescue_chroot_start = "Chroot kabuğu başlatılıyor…"
rescue_grub = "🔧 GRUB'u Yeniden Kur"
rescue_grub_start = "GRUB yeniden kuruluyor…"
rescue_return = "↩ Normal Kuruluma Dön"
rescue_not_available = "Kurtarma modu: mevcut Linux kurulumu bulunamadı."
rescue_pisi_history = "📜 Pisi Geçmişi Geri Alma"
rescue_pisi_history_title = "Pisi Paket Geçmişi"
rescue_pisi_history_desc = "Bir geçmiş kaydı seçin ve bu noktaya dönmek için Geri Al'a tıklayın."
rescue_pisi_history_scan = "🔍 Geçmişi Listele"
rescue_pisi_history_empty = "Paket geçmişi bulunamadı veya okunamadı."
rescue_pisi_takeback = "↩ Bu Noktaya Geri Al"
rescue_pisi_takeback_start = "Pisi takeback çalıştırılıyor…"
rescue_pisi_history_ok = "Geri alma başarıyla tamamlandı."
rescue_pisi_history_err = "Hata: %{err}"
# ── LUKS ────────────────────────────────────────────────────
luks_enable_root = "Kök bölümünü şifrele (LUKS)"
luks_password = "Şifreleme parolası:"
luks_password_weak = "Parola çok kısa (en az 4 karakter)"
# ── Netinstall ──────────────────────────────────────────────
netinstall = "Yazılım Seçimi"
netinstall_title = "Ek Yazılım"
netinstall_description = "Kurulacak ek paket gruplarını seçin."
netinstall_package_count = "paket"
netinstall_selected = "seçildi"
# ── Medya Kontrolü ──────────────────────────────────────────
media_check = "Medya Kontrolü"
media_check_title = "Kurulum Medyasını Doğrula"
media_check_description = "ISO veya USB sürücünüzün bütünlüğünü kontrol edin."
media_check_scan = "🔍 Medyayı Tara"
media_check_select = "Doğrulanacak dosyayı seçin:"
media_check_running = "Kontrol ediliyor… lütfen bekleyin."
media_check_ok = "✅ Medya bütünlüğü başarıyla doğrulandı."
media_check_fail = "❌ Medya bütünlük kontrolü BAŞARISIZ! Lütfen yeniden indirin."
media_check_no_md5 = "⚠️ md5sum veya sha256sum aracı bulunamadı."
media_check_file_not_found = "⚠️ Seçilen dosya bulunamadı."
media_check_path_label = "Dosya yolu:"
media_check_path_hint = "örn. /run/live/medium/pisilinux.iso"
media_check_algorithm = "Algoritma:"
job_media_check = "Kurulum medyası doğrulanıyor"
# ── LVM ─────────────────────────────────────────────────────
lvm_lv_title = "LVM Mantıksal Birimler"
lvm_lv_name = "LV Adı"
lvm_lv_vg = "Birim Grubu"
lvm_lv_add = "Mantıksal Birim Ekle"
lvm_lv_error_vg = "En az bir Birim Grubu tanımlanmalıdır."
lvm_lv_error_size = "Girilen boyut, Birim Grubu kapasitesini aşıyor."
lvm_lv_mountpoint = "Bağlama Noktası"
lvm_lv_fstype = "Dosya Sistemi"
lvm_lv_size = "Boyut (MB, 0=kalan):"
lvm_lv_action = "İşlem"
# ── CLI / Otomatik Kurulum ─────────────────────────────────
hostname = "Bilgisayar adı"
timezone = "Zaman dilimi"
username = "Kullanıcı adı"
# ── Slayt Gösterisi (yedek) ────────────────────────────────
welcome_slide_title = "Pisi Linux'a Hoş Geldiniz"
welcome_slide_description = "Modern, hızlı ve güvenli bir GNU/Linux dağıtımı."
package_manager_title = "Pisi Paket Yöneticisi"
package_manager_description = "Bağımlılık çözümü, geçmiş geri alma ve atomik güncellemeler ile güçlü bir paket yöneticisi."
community_title = "Topluluk Odaklı"
community_description = "Topluluk tarafından, topluluk için geliştirilmiştir. Pisi Linux özgür ve açık kaynaktır."
speed_title = "Hız İçin Optimize Edilmiş"
speed_description = "Hem modern hem de eski donanımlarda hızlı çalışacak şekilde tasarlanmıştır."
security_title = "Önce Güvenlik"
security_description = "Düzenli güvenlik güncellemeleri ve sağlam bir izin sistemi sisteminizi güvende tutar."
customization_title = "Tam Özelleştirme"
customization_description = "Kurulum sırasında masaüstünüzü, uygulamalarınızı ve görünümünüzü seçin."
budgie_title = "Budgie Masaüstü"
budgie_description = "Sadeliği hedefleyen modern ve şık bir masaüstü ortamı."
budgie_applets_title = "Budgie Applet'leri"
budgie_applets_description = "Bildirimler, sistem tepsisi ve daha fazlası için özelleştirilebilir applet'ler."
budgie_raven_title = "Budgie Raven"
budgie_raven_description = "Hızlı erişim için bildirim merkezi ve ayar paneli."
budgie_gtk_theme_title = "GTK Tema Entegrasyonu"
budgie_gtk_theme_description = "Tutarlı uygulama görünümü için kusursuz GTK tema desteği."
cinnamon_title = "Cinnamon Masaüstü"
cinnamon_description = "Modern özelliklere sahip geleneksel bir masaüstü düzeni."
cinnamon_panel_title = "Cinnamon Paneli"
cinnamon_panel_description = "Applet'ler, başlatıcılar ve tepsi simgeleri ile esnek panel sistemi."
cinnamon_settings_title = "Cinnamon Ayarları"
cinnamon_settings_description = "Menüden erişilebilen kapsamlı sistem ayarları."
cinnamon_desklets_title = "Cinnamon Desklet'leri"
cinnamon_desklets_description = "Hava durumu, saat, sistem izleme ve daha fazlası için etkileşimli desklet'ler."
gnome_title = "GNOME Masaüstü"
gnome_description = "Sade, dikkat dağıtmayan bir masaüstü ortamı."
gnome_search_title = "GNOME Arama"
gnome_search_description = "Uygulamalar, dosyalar ve ayarlar arasında anında arama."
gnome_extensions_title = "GNOME Eklentileri"
gnome_extensions_description = "Topluluk tarafından geliştirilen eklentilerle masaüstünüzü genişletin."
kde_title = "KDE Plasma Masaüstü"
kde_description = "Özellik dolu, son derece özelleştirilebilir masaüstü ortamı."
dolphin_title = "Dolphin Dosya Yöneticisi"
dolphin_description = "Bölünmüş ekran görünümü ve güçlü arama ile sezgisel dosya yöneticisi."
kde_connect_title = "KDE Connect"
kde_connect_description = "Telefonunuzu masaüstünüzle kusursuzca bütünleştirin."
discover_title = "Discover Yazılım Merkezi"
discover_description = "Tek tıkla uygulamalara göz atın ve kurun."
lxde_title = "LXDE Masaüstü"
lxde_description = "Eski donanımlar için hafif masaüstü ortamı."
lxde_openbox_title = "OpenBox Pencere Yöneticisi"
lxde_openbox_description = "Klavye odaklı iş akışı ile hızlı ve hafif pencere yöneticisi."
lxde_pcmanfm_title = "PCManFM Dosya Yöneticisi"
lxde_pcmanfm_description = "Sekmeli gezinti ve masaüstü yönetimi ile hafif dosya yöneticisi."
lxde_lxpanel_title = "LXPanel"
lxde_lxpanel_description = "Uygulama menüsü, görev çubuğu ve sistem tepsisi ile hafif panel."
lxqt_title = "LXQt Masaüstü"
lxqt_description = "Qt tabanlı hafif bir masaüstü ortamı."
lxqt_panel_title = "LXQt Paneli"
lxqt_panel_description = "Araçlar ve başlatıcılar ile son derece yapılandırılabilir panel."
lxqt_pcmanfm_qt_title = "PCManFM-Qt Dosya Yöneticisi"
lxqt_pcmanfm_qt_description = "Sekmeli arayüz ve masaüstü simgeleri ile hızlı dosya yöneticisi."
lumina_title = "Lumina Masaüstü"
lumina_description = "TrueOS'tan hafif, eklenti tabanlı bir masaüstü ortamı."
lumina_applications_title = "Lumina Uygulamaları"
lumina_applications_description = "Lumina masaüstü ortamı için yerel uygulamalar."
lumina_theme_title = "Lumina Tema Motoru"
lumina_theme_description = "Tutarlı bir görsel deneyim için tam tema desteği."
lumina_panel_title = "Lumina Paneli"
lumina_panel_description = "Özel araçlar ile esnek panel sistemi."
mate_title = "MATE Masaüstü"
mate_description = "GNOME 2'den devam eden geleneksel masaüstü ortamı."
mate_caja_title = "Caja Dosya Yöneticisi"
mate_caja_description = "Uzamsal ve tarayıcı görünümleri ile özellik dolu dosya yöneticisi."
mate_settings_title = "MATE Ayarları"
mate_settings_description = "Kategoriye göre düzenlenmiş komple sistem ayarları."
mate_applications_title = "MATE Uygulamaları"
mate_applications_description = "Günlük işler için bütünleşik uygulama paketi."
xfce_title = "XFCE Masaüstü"
xfce_description = "Hafif ancak güçlü bir masaüstü ortamı."
xfce_panel_title = "XFCE Paneli"
xfce_panel_description = "Başlatıcılar, applet'ler ve menüler ile son derece özelleştirilebilir panel."
xfce_thunar_title = "Thunar Dosya Yöneticisi"
xfce_thunar_description = "Toplu yeniden adlandırma ve eklentiler ile hızlı ve güvenilir dosya yöneticisi."
xfce_settings_title = "XFCE Ayarları"
xfce_settings_description = "Masaüstü özelleştirmesi için kapsamlı ayar yöneticisi."
+86 -18
View File
@@ -81,12 +81,8 @@ fn check_ram() -> CheckResult {
/// En az 10 GB boş disk alanı kontrolü.
fn check_disk_space() -> CheckResult {
use sysinfo::Disks;
let disks = Disks::new_with_refreshed_list();
let max_gb = disks.iter()
.map(|d| d.total_space() / 1024 / 1024 / 1024)
.max()
.unwrap_or(0);
// sysinfo yerine doğrudan kernel'in gördüğü blok aygıtı boyutunu alıyoruz.
let max_gb = get_max_block_device_gb();
if max_gb < 10 {
CheckResult::fail(
@@ -99,10 +95,82 @@ fn check_disk_space() -> CheckResult {
t!("check_disk_warn", size = max_gb),
)
} else {
CheckResult::pass(t!("check_disk_space"), t!("check_disk_pass", size = max_gb))
CheckResult::pass(
t!("check_disk_space"),
t!("check_disk_pass", size = max_gb)
)
}
}
use std::fs;
/// Sistemdeki en büyük fiziksel/sanal blok aygıtının boyutunu GB cinsinden döndürür.
///
/// Önce `lsblk -d` ile dener (yalnızca disk düzeyindeki aygıtlar, bölümler hariç).
/// lsblk yoksa veya başarısız olursa `/sys/block` ile fallback yapar;
/// `/sys/block` yalnızca tam diskleri içerdiğinden bölüm girişleri karışmaz.
fn get_max_block_device_gb() -> u64 {
let mut max_gb = 0u64;
// ── 1. Birincil yöntem: lsblk -d (partition.rs ile tutarlı) ──────────────
let lsblk_out = std::process::Command::new("lsblk")
.args(["-d", "-o", "NAME,SIZE", "-b", "--noheadings"])
.output();
if let Ok(o) = lsblk_out {
if o.status.success() {
let text = String::from_utf8_lossy(&o.stdout);
for line in text.lines() {
let mut tokens = line.split_whitespace();
let name = match tokens.next() { Some(n) => n, None => continue };
// loop, ram, zram, sr (CD-ROM) aygıtlarını atla
if name.starts_with("loop")
|| name.starts_with("ram")
|| name.starts_with("sr")
|| name.starts_with("zram")
{ continue; }
let size_str = match tokens.next() { Some(s) => s, None => continue };
let size_bytes: u64 = size_str.parse().unwrap_or(0);
if size_bytes == 0 { continue; }
let size_gb = size_bytes / 1_073_741_824;
if size_gb > max_gb { max_gb = size_gb; }
}
// lsblk başarılıysa ve en az bir disk bulduysa döndür
if max_gb > 0 { return max_gb; }
}
}
// ── 2. Fallback: /sys/block (yalnızca tam diskler — bölüm yok) ───────────
// /sys/class/block aksine /sys/block dizini YALNIZCA tam disk girişlerini
// içerir; sda1, nvme0n1p1 gibi bölüm sembolik bağlantıları burada bulunmaz.
if let Ok(entries) = fs::read_dir("/sys/block") {
for entry in entries.flatten() {
let name = entry.file_name().into_string().unwrap_or_default();
if name.starts_with("loop")
|| name.starts_with("ram")
|| name.starts_with("zram")
|| name.starts_with("sr")
{ continue; }
let size_path = entry.path().join("size");
if let Ok(size_str) = fs::read_to_string(&size_path) {
// sysfs 'size' dosyası: 512 byte'lık sektör sayısı
if let Ok(sectors) = size_str.trim().parse::<u64>() {
let size_gb = (sectors * 512) / 1_073_741_824;
if size_gb > max_gb { max_gb = size_gb; }
}
}
}
}
max_gb
}
/// İnternet bağlantısı kontrolü (isteğe bağlı).
fn check_internet() -> CheckResult {
// DNS çözümlemesi ile basit kontrol
@@ -150,19 +218,19 @@ fn check_cpu_arch() -> CheckResult {
}
}
/// Canlı sistem ortamı kontrolü.
/// Canlı sistem ortamı kontrolü — PyQt yali'deki gibi:
/// /bootmnt varsa kaynak orası (/bootmnt/pisi/pisi.sqfs),
/// yoksa /mnt/cdrom/pisi/pisi.sqfs kontrol edilir.
fn check_live_env() -> CheckResult {
let source = "/run/livecd/squashfs-root";
if !Path::new(source).exists() {
CheckResult::fail(
t!("check_live_sys"),
t!("check_live_fail"),
)
let sqfs = if Path::new("/bootmnt").exists() {
"/bootmnt/pisi/pisi.sqfs"
} else {
CheckResult::pass(
t!("check_live_sys"),
t!("check_live_pass"),
)
"/mnt/cdrom/pisi/pisi.sqfs"
};
if Path::new(sqfs).exists() {
CheckResult::pass(t!("check_live_sys"), t!("check_live_pass"))
} else {
CheckResult::fail(t!("check_live_sys"), t!("check_live_fail"))
}
}
+99 -183
View File
@@ -1,216 +1,132 @@
//! Otomatik kurulum (answer file) desteği.
//! Answer file yapıları, yükleme ve state'e uygulama.
//!
//! Kullanım:
//! yali-rs --auto-install /path/to/answer.toml
//!
//! Answer file formatı (TOML):
//!
//! [install]
//! disk = "/dev/sda"
//! timezone = "Europe/Istanbul"
//! locale = "tr_TR.UTF-8"
//! keyboard = "tr"
//!
//! [user]
//! username = "pisi"
//! password = "changeme"
//! hostname = "pisilinux"
use serde::Deserialize;
use std::path::Path;
//! Job kuyruğu ve Job struct'ları `src/jobs/` modülündedir.
pub mod checker;
// ─────────────────────────────────────────────
// ANSWER FILE YAPISI
// ANSWER FILE: VERİ YAPILARI
// ─────────────────────────────────────────────
#[derive(Debug, Deserialize)]
/// `[install]` bölümündeki alanlar.
#[derive(Debug, serde::Deserialize)]
pub struct InstallConfig {
pub disk: String,
#[serde(default = "default_timezone")]
pub timezone: String,
#[serde(default = "default_locale")]
pub locale: String,
#[serde(default = "default_keyboard")]
pub keyboard: String,
#[serde(default)]
pub keyboard_variant: String,
#[serde(default = "default_source")]
pub source: String,
#[serde(default = "default_mount")]
pub mount: String,
}
/// `[user]` bölümündeki alanlar.
#[derive(Debug, serde::Deserialize)]
pub struct UserConfig {
pub username: String,
pub password: String,
#[serde(default = "default_hostname")]
pub hostname: String,
}
/// Answer file'ın tamamı (`--auto-install <dosya.toml>`).
#[derive(Debug, serde::Deserialize)]
pub struct AnswerFile {
pub install: InstallConfig,
pub user: UserConfig,
}
#[derive(Debug, Deserialize)]
pub struct InstallConfig {
/// Hedef disk aygıtı. Örn: "/dev/sda", "/dev/nvme0n1"
pub disk: String,
/// Zaman dilimi. Örn: "Europe/Istanbul"
#[serde(default = "default_timezone")]
pub timezone: String,
/// Sistem locale. Örn: "tr_TR.UTF-8"
#[serde(default = "default_locale")]
pub locale: String,
/// Klavye düzeni. Örn: "tr"
#[serde(default = "default_keyboard")]
pub keyboard: String,
/// Klavye varyantı. Örn: "f" (Türkçe F için)
#[serde(default)]
pub keyboard_variant: String,
/// Kaynak dizin (canlı sistem). Varsayılan: /run/livecd/squashfs-root/
#[serde(default = "default_source")]
pub source: String,
/// Hedef bağlama noktası. Varsayılan: /mnt
#[serde(default = "default_mount")]
pub mount: String,
}
#[derive(Debug, Deserialize)]
pub struct UserConfig {
pub username: String,
pub password: String,
pub hostname: String,
}
fn default_timezone() -> String { "Europe/Istanbul".into() }
fn default_locale() -> String { "tr_TR.UTF-8".into() }
fn default_keyboard() -> String { "tr".into() }
fn default_source() -> String { "/run/livecd/squashfs-root/".into() }
fn default_mount() -> String { "/mnt".into() }
fn default_timezone() -> String { "Europe/Istanbul".to_string() }
fn default_locale() -> String { "tr_TR.UTF-8".to_string() }
fn default_keyboard() -> String { "tr".to_string() }
fn default_source() -> String { "/run/livecd/squashfs-root/".to_string() }
fn default_mount() -> String { "/mnt".to_string() }
fn default_hostname() -> String { "pisilinux".to_string() }
// ─────────────────────────────────────────────
// DOĞRULAMA
// ANSWER FILE: YÜKLE ve UYGULA
// ─────────────────────────────────────────────
#[derive(Debug)]
pub enum AnswerError {
Io(std::io::Error),
Parse(toml::de::Error),
Validation(Vec<String>),
}
impl std::fmt::Display for AnswerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AnswerError::Io(e) => write!(f, "Dosya okuma hatası: {}", e),
AnswerError::Parse(e) => write!(f, "TOML ayrıştırma hatası: {}", e),
AnswerError::Validation(v) => write!(f, "Doğrulama hataları:\n{}", v.join("\n")),
}
}
}
/// Answer dosyasını okuyup ayrıştırır ve doğrular.
pub fn load(path: &Path) -> Result<AnswerFile, AnswerError> {
/// TOML answer dosyasını diskten okur ve ayrıştırır.
pub fn load(path: &std::path::Path) -> Result<AnswerFile, String> {
let content = std::fs::read_to_string(path)
.map_err(AnswerError::Io)?;
let af: AnswerFile = toml::from_str(&content)
.map_err(AnswerError::Parse)?;
validate(&af)?;
Ok(af)
.map_err(|e| format!("Answer file okunamadı '{}': {}", path.display(), e))?;
toml::from_str(&content)
.map_err(|e| format!("Answer file ayrıştırılamadı: {}", e))
}
fn validate(af: &AnswerFile) -> Result<(), AnswerError> {
let mut errors: Vec<String> = Vec::new();
/// `AnswerFile` içeriğini `GlobalState`'e uygular.
/// Disk boyutunu `lsblk` ile sorgular ve `partition_plan` oluşturur.
pub fn apply_to_state(af: &AnswerFile, state: &mut crate::installer::GlobalState) {
use crate::installer::{DiskInfo, PartitionPlan, PartitionTableType};
// Disk kontrolü
if !af.install.disk.starts_with("/dev/") {
errors.push(format!(
"Geçersiz disk yolu '{}'. '/dev/' ile başlamalı.",
af.install.disk
));
}
if !Path::new(&af.install.disk).exists() {
errors.push(format!(
"Disk '{}' bulunamadı. Mevcut diskler için 'lsblk' komutunu çalıştırın.",
af.install.disk
));
}
// Kullanıcı adı doğrulaması (UsersStep ile aynı kural)
let un = &af.user.username;
if un.is_empty() || un.len() > 32 {
errors.push("Kullanıcı adı 132 karakter arasında olmalı.".into());
} else {
let valid = un.chars().next().map(|c| c.is_ascii_lowercase() || c == '_').unwrap_or(false)
&& un.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-');
if !valid {
errors.push(format!(
"Geçersiz kullanıcı adı '{}'. Yalnızca küçük harf, rakam, - ve _ kullanılabilir.",
un
));
}
}
// Şifre uzunluğu
if af.user.password.len() < 6 {
errors.push("Şifre en az 6 karakter olmalı.".into());
}
// Hostname
let hn = &af.user.hostname;
if hn.is_empty() || hn.len() > 63 || hn.starts_with('-') || hn.ends_with('-')
|| !hn.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
{
errors.push(format!("Geçersiz hostname '{}'.", hn));
}
if errors.is_empty() { Ok(()) } else { Err(AnswerError::Validation(errors)) }
}
// ─────────────────────────────────────────────
// GLOBALSTATE'E UYGULA
// ─────────────────────────────────────────────
use crate::installer::GlobalState;
use crate::steps::partition::PartitionStep;
use crate::installer::InstallerStep;
/// Answer file verilerini `GlobalState`'e yazar.
/// Bu işlemden sonra `PartitionStep::on_enter()` çağrılarak
/// disk listesi yenilenebilir ve plan oluşturulabilir.
pub fn apply_to_state(af: &AnswerFile, state: &mut GlobalState) {
// Temel alanlar
state.timezone = af.install.timezone.clone();
state.keyboard_layout = af.install.keyboard.clone();
state.keyboard_variant = af.install.keyboard_variant.clone();
state.selected_disk = Some(af.install.disk.clone());
state.erase_disk = true;
state.username = af.user.username.clone();
state.password = af.user.password.clone();
state.password_confirm = af.user.password.clone();
state.hostname = af.user.hostname.clone();
state.selected_disk = Some(af.install.disk.clone());
state.erase_disk = true;
state.language = if af.install.locale.starts_with("tr") { "tr" } else { "en" }.to_string();
// Locale'den dil kodunu çıkar: "tr_TR.UTF-8" → "tr"
state.language = af.install.locale
.split('_')
.next()
.unwrap_or("tr")
.to_string();
// Disk boyutunu lsblk ile sorgula
let disk_info: Option<DiskInfo> = (|| {
let out = std::process::Command::new("lsblk")
.args(["-d", "-b", "-o", "NAME,SIZE,MODEL", "--noheadings", &af.install.disk])
.output()
.ok()?;
if !out.status.success() { return None; }
let text = String::from_utf8_lossy(&out.stdout);
let line = text.lines().next()?;
let mut parts = line.split_whitespace();
let name = parts.next()?.to_string();
let bytes: u64 = parts.next()?.parse().ok()?;
let model = parts.collect::<Vec<_>>().join(" ");
Some(DiskInfo {
name: format!("/dev/{}", name),
model: if model.is_empty() { "Unknown".to_string() } else { model },
size_gb: bytes / 1_073_741_824,
size_bytes: bytes,
})
})();
// Bölümleme planını hesapla
let is_uefi = Path::new("/sys/firmware/efi").exists();
let mut ps = PartitionStep::default();
ps.on_enter(state);
// Partition planını hesapla
if let Some(info) = disk_info {
let is_uefi = std::path::Path::new("/sys/firmware/efi").exists();
let efi_mb = if is_uefi { 512u64 } else { 0 };
let total_mb = info.size_bytes / 1024 / 1024;
// Seçili diski bul ve plan oluştur
if let Some(disk) = state.available_disks.iter().find(|d| d.name == af.install.disk).cloned() {
use crate::installer::{PartitionPlan, PartitionTableType};
let total_mb = disk.size_bytes / 1024 / 1024;
let efi_mb = if is_uefi { 512 } else { 0 };
let swap_mb = recommended_swap_mb();
let root_mb = total_mb.saturating_sub(efi_mb + swap_mb + 512);
state.partition_plan = Some(PartitionPlan {
disk: disk.name.clone(),
table_type: if is_uefi { PartitionTableType::Gpt } else { PartitionTableType::Mbr },
efi_mb,
swap_mb,
root_mb,
});
// Tavsiye edilen swap: RAM * 1.5, min 512 MB, max 8192 MB
let mut sys = sysinfo::System::new();
sys.refresh_memory();
let ram_mb = sys.total_memory() / 1024 / 1024;
let swap_mb = (ram_mb * 3 / 2).clamp(512, 8192);
let overhead = efi_mb + swap_mb + 512;
let root_mb = total_mb.saturating_sub(overhead);
if root_mb >= 6144 && info.size_gb >= 10 {
state.partition_plan = Some(PartitionPlan {
disk: info.name.clone(),
table_type: if is_uefi { PartitionTableType::Gpt } else { PartitionTableType::Mbr },
efi_mb,
swap_mb,
root_mb,
encrypt_root: false,
luks_password: String::new(),
});
}
state.available_disks.push(info);
}
}
fn recommended_swap_mb() -> u64 {
use sysinfo::System;
let mut sys = System::new();
sys.refresh_memory();
let ram_mb = sys.total_memory() / 1024 / 1024;
let swap = if ram_mb <= 2048 { ram_mb } else if ram_mb <= 8192 { ram_mb / 2 } else { 4096 };
swap.clamp(512, 8192)
}
+255
View File
@@ -0,0 +1,255 @@
//! Yali-RS görsel özelleştirme (branding) yapılandırması.
//!
//! `branding.toml` dosyasını okur ve tüm sabit kodlanmış görsel yollarını
//! çalışma zamanında çözümler. Geliştiriciler yali-rs koduna dokunmadan
//! yalnızca `branding.toml` ile logo, slayt görseli ve renk değiştirebilir.
#![allow(dead_code)]
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Clone, Debug, Default, Deserialize)]
pub struct BrandingConfig {
#[serde(default)]
pub general: GeneralConfig,
#[serde(default)]
pub slides: SlidesConfig,
#[serde(default)]
pub theme: ThemeConfig,
#[serde(default)]
pub oem: OemConfig,
}
// ── [general] ────────────────────────────────────────────────────────────────
#[derive(Clone, Debug, Deserialize)]
pub struct GeneralConfig {
/// Kurulum penceresinin başlığı.
#[serde(default = "default_title")]
pub title: String,
/// Sol kenar çubuğunda gösterilen dağıtım ismi.
#[serde(default = "default_subtitle")]
pub subtitle: String,
/// Sol kenar çubuğu için Yali logosu dosya yolu.
#[serde(default = "default_yali_logo")]
pub yali_logo: String,
/// Kurulum ana sayfasında gösterilen PisiLinux logosu (koyu tema).
#[serde(default = "default_pisi_logo_dark")]
pub pisi_logo_dark: String,
/// Kurulum ana sayfasında gösterilen PisiLinux logosu (açık tema).
#[serde(default = "default_pisi_logo_light")]
pub pisi_logo_light: String,
}
impl Default for GeneralConfig {
fn default() -> Self {
Self {
title: default_title(),
subtitle: default_subtitle(),
yali_logo: default_yali_logo(),
pisi_logo_dark: default_pisi_logo_dark(),
pisi_logo_light: default_pisi_logo_light(),
}
}
}
// ── [slides] ─────────────────────────────────────────────────────────────────
/// Tek bir slaytın içeriği.
#[derive(Clone, Debug, Default, Deserialize)]
pub struct SlideItem {
/// [slides.icons] tablosundaki ikon anahtar adı.
pub icon: String,
/// Slayt başlığı.
pub title: String,
/// Slayt açıklama metni.
pub description: String,
/// Yalnızca bu masaüstü ortamında göster (boş = hepsinde).
/// Örnek: "xfce", "kde", "gnome"
#[serde(default)]
pub desktop: String,
}
#[derive(Clone, Debug, Default, Deserialize)]
pub struct SlidesConfig {
/// Slayt geçiş süresi (saniye).
#[serde(default = "default_interval")]
pub interval_secs: u64,
/// Slayt ikonu anahtar sözcüğü → dosya yolu eşlemesi.
#[serde(default)]
pub icons: HashMap<String, String>,
/// branding.toml içinde [[slides.items]] ile tanımlanan slayt listesi.
/// Boşsa slideshow.rs içindeki sabit slaytlar kullanılır.
#[serde(default)]
pub items: Vec<SlideItem>,
}
// ── [theme] ──────────────────────────────────────────────────────────────────
#[derive(Clone, Debug, Deserialize)]
pub struct ThemeConfig {
#[serde(default = "default_primary")]
pub primary: String,
#[serde(default = "default_accent")]
pub accent: String,
#[serde(default = "default_background")]
pub background: String,
#[serde(default = "default_text")]
pub text: String,
}
impl Default for ThemeConfig {
fn default() -> Self {
Self {
primary: default_primary(),
accent: default_accent(),
background: default_background(),
text: default_text(),
}
}
}
// ── [oem] ────────────────────────────────────────────────────────────────────
#[derive(Clone, Debug, Default, Deserialize)]
pub struct OemConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
#[allow(dead_code)]
pub skip_steps: Vec<usize>,
#[serde(default)]
pub default_username: String,
#[serde(default = "default_hostname")]
pub default_hostname: String,
#[serde(default)]
pub default_password: String,
}
// ── Varsayılan değerler ───────────────────────────────────────────────────────
fn default_title() -> String { "YALI Installation Tool".to_string() }
fn default_subtitle() -> String { "PiSi GNU/LINUX".to_string() }
fn default_yali_logo() -> String { "assets/yali.png".to_string() }
fn default_pisi_logo_dark() -> String { "assets/pisi-logo-dark.png".to_string() }
fn default_pisi_logo_light() -> String { "assets/pisi-logo-light.png".to_string() }
fn default_interval() -> u64 { 6 }
fn default_primary() -> String { "#1a73e8".to_string() }
fn default_accent() -> String { "#34a853".to_string() }
fn default_background() -> String { "#181824".to_string() }
fn default_text() -> String { "#e2e8f0".to_string() }
fn default_hostname() -> String { "pisi-oem".to_string() }
// ── Yükleme ──────────────────────────────────────────────────────────────────
impl BrandingConfig {
/// `branding.toml` dosyasını arar ve yükler.
///
/// Arama sırası:
/// 1. `/etc/yali/branding.toml`
/// 2. `/usr/share/yali/branding.toml`
/// 3. Çalışma dizinindeki `branding.toml`
pub fn load() -> Self {
let paths = [
"/etc/yali/branding.toml",
"/usr/share/yali/branding.toml",
"branding.toml",
];
for path in &paths {
if let Ok(content) = std::fs::read_to_string(path) {
match toml::from_str(&content) {
Ok(cfg) => return cfg,
Err(e) => eprintln!("[branding] {}: {}", path, e),
}
}
}
BrandingConfig::default()
}
/// Belirtilen dile ait `branding_{lang}.toml` dosyasını arar ve yükler.
/// Bulunamazsa varsayılan `branding.toml` dosyasına geri döner.
pub fn load_for_lang(lang: &str) -> Self {
let lang_paths = [
format!("/etc/yali/branding_{}.toml", lang),
format!("/usr/share/yali/branding_{}.toml", lang),
format!("branding_{}.toml", lang),
];
for path in &lang_paths {
if let Ok(content) = std::fs::read_to_string(path) {
match toml::from_str(&content) {
Ok(cfg) => return cfg,
Err(e) => eprintln!("[branding] {}: {}", path, e),
}
}
}
Self::load()
}
/// İkon adına göre dosya yolunu döner.
///
/// Koyu/açık temaya duyarlı `pisilogo` ikonu için `is_dark` parametresi
/// kullanılır; diğer ikonlar için görmezden gelinir.
pub fn icon_path(&self, icon: &str, is_dark: bool) -> Option<String> {
// pisilogo özel durumu: tema bazlı iki ayrı anahtar
if icon == "pisilogo" {
let key = if is_dark { "pisi_logo_dark" } else { "pisi_logo_light" };
return self.slides.icons.get(key).cloned();
}
self.slides.icons.get(icon).cloned()
}
/// Sol kenar çubuğu (Yali) logosu için `file://` URI döner.
pub fn yali_logo_uri(&self) -> String {
path_to_file_uri(&self.general.yali_logo)
}
/// Ana sayfa PisiLinux logosu (koyu tema) için `file://` URI döner.
pub fn pisi_logo_dark_uri(&self) -> String {
path_to_file_uri(&self.general.pisi_logo_dark)
}
/// Ana sayfa PisiLinux logosu (açık tema) için `file://` URI döner.
pub fn pisi_logo_light_uri(&self) -> String {
path_to_file_uri(&self.general.pisi_logo_light)
}
}
/// Göreli veya mutlak dosya yolunu `egui::Image::from_uri` için uygun
/// `file://` URI formatına çevirir.
pub fn path_to_file_uri(path: &str) -> String {
if path.starts_with("file://") || path.starts_with("http") {
return path.to_string();
}
if std::path::Path::new(path).is_absolute() {
return format!("file://{}", path);
}
// Göreli yolları sırasıyla ara:
// 1. Çalışma dizini (CWD)
// 2. /usr/share/yali/
// 3. /etc/yali/
let cwd = std::env::current_dir().unwrap_or_default();
let cwd_path = cwd.join(path);
if cwd_path.exists() {
return format!("file://{}", cwd_path.display());
}
let usr_path = std::path::Path::new("/usr/share/yali").join(path);
if usr_path.exists() {
return format!("file://{}", usr_path.display());
}
let etc_path = std::path::Path::new("/etc/yali").join(path);
if etc_path.exists() {
return format!("file://{}", etc_path.display());
}
// Hiçbiri bulunamazsa varsayılan olarak CWD yolunu döner (hata/fallback takibi için)
format!("file://{}", cwd_path.display())
}
+391 -27
View File
@@ -1,4 +1,5 @@
use eframe::egui;
use sysinfo::System;
use rust_i18n::t;
/// Disk adı ve bölüm numarasından tam aygıt yolunu oluşturur.
@@ -45,6 +46,8 @@ pub struct PartitionPlan {
pub efi_mb: u64, // 0 → BIOS, >0 → UEFI
pub swap_mb: u64,
pub root_mb: u64, // kalan alan
pub encrypt_root: bool, // Kök bölümü LUKS ile şifrelensin mi?
pub luks_password: String, // LUKS şifresi
}
#[derive(Clone, Debug, Default, PartialEq)]
@@ -67,23 +70,31 @@ impl std::fmt::Display for PartitionTableType {
#[derive(Clone, Debug, PartialEq)]
pub enum FsType {
Ext4,
Ext3,
Btrfs,
Xfs,
Fat32, // EFI bölümü için
Ntfs,
Swap,
Lvm, // LVM fiziksel bölüm (PV)
#[allow(dead_code)]
LvmLv, // LVM mantıksal bölüm (LV)
}
impl FsType {
pub fn label(&self) -> &'static str {
match self {
FsType::Ext4 => "ext4",
FsType::Ext3 => "ext3",
FsType::Btrfs => "btrfs",
FsType::Xfs => "xfs",
FsType::Fat32 => "FAT32 (EFI)",
FsType::Ntfs => "NTFS",
FsType::Swap => "swap",
FsType::Lvm => "LVM PV",
FsType::LvmLv => "LVM LV",
}
}
}
impl std::fmt::Display for FsType {
@@ -92,6 +103,25 @@ impl std::fmt::Display for FsType {
}
}
/// LVM Volume Group yapılandırması.
#[derive(Clone, Debug, Default)]
pub struct VolumeGroup {
pub name: String,
pub pv_devices: Vec<String>, // PV olarak kullanılacak bölüm aygıtları
pub size_mb: u64, // VG toplam boyutu (hesaplanan)
}
/// LVM Logical Volume yapılandırması.
#[derive(Clone, Debug)]
pub struct LogicalVolume {
pub name: String,
pub vg_name: String,
pub size_mb: u64, // 0 = kalan alan
pub fstype: FsType,
#[allow(dead_code)]
pub mountpoint: String,
}
/// Kullanıcının manuel olarak tanımladığı tek bir bölüm.
#[derive(Clone, Debug)]
pub struct CustomPartition {
@@ -103,6 +133,12 @@ pub struct CustomPartition {
pub fstype: FsType,
/// Bağlama noktası; swap için "swap"
pub mountpoint: String,
/// LUKS şifreleme aktif mi?
pub encrypt: bool,
/// LUKS şifresi
pub luks_password: String,
/// LUKS harici adı (ör. "cryptroot")
pub luks_name: String,
}
/// Kurulum boyunca toplanan tüm veriler.
@@ -114,6 +150,8 @@ pub struct GlobalState {
pub partition_plan: Option<PartitionPlan>,
/// Manuel bölümleme modu için kullanıcının tanımladığı bölümler.
pub custom_partitions: Vec<CustomPartition>,
pub volume_groups: Vec<VolumeGroup>,
pub logical_volumes: Vec<LogicalVolume>,
pub keyboard_layout: String,
pub keyboard_variant: String,
pub username: String,
@@ -126,6 +164,44 @@ pub struct GlobalState {
pub install_log: Vec<String>,
pub demo_mode: bool,
pub is_dark: bool,
pub rescue_mode: bool,
pub bootloader_device: String,
pub bootloader_timeout: u32,
pub bootloader_password: String,
pub kernel_options: String,
pub license_accepted: bool,
pub root_password: String,
pub root_password_confirm: String,
pub display_manager: String,
pub autologin: bool,
pub desktop_environment: String,
pub network_wifi_password: String,
pub network_http_proxy: String,
pub network_https_proxy: String,
pub network_ftp_proxy: String,
pub use_ntp: bool,
pub manual_year: i32,
pub manual_month: u32,
pub manual_day: u32,
pub manual_hour: u32,
pub manual_minute: u32,
#[allow(dead_code)]
pub encrypt_root: bool,
#[allow(dead_code)]
pub luks_password: String,
pub oem_mode: bool,
pub selected_package_groups: Vec<String>,
/// branding.toml [slides.icons] tablosundan yüklenen ikon → dosya URI eşlemesi.
/// Slideshow bu map üzerinden çalışma zamanında görsel yollarını çözümler.
pub branding_icons: std::collections::HashMap<String, String>,
/// Sidebar için koyu tema logo URI'si.
pub branding_yali_dark: String,
pub branding_pisi_dark: String,
/// Sidebar için açık tema logo URI'si.
pub branding_yali_light: String,
pub branding_pisi_light: String,
/// branding.toml dosyasından yüklenen slaytlar.
pub branding_slides: Vec<crate::branding::SlideItem>,
}
impl Default for GlobalState {
@@ -137,6 +213,8 @@ impl Default for GlobalState {
erase_disk: true,
partition_plan: None,
custom_partitions: Vec::new(),
volume_groups: Vec::new(),
logical_volumes: Vec::new(),
keyboard_layout: "tr".to_string(),
keyboard_variant: String::new(),
username: String::new(),
@@ -149,11 +227,51 @@ impl Default for GlobalState {
install_log: Vec::new(),
demo_mode: false,
is_dark: true,
rescue_mode: false,
bootloader_device: String::new(),
bootloader_timeout: 5,
bootloader_password: String::new(),
kernel_options: String::new(),
license_accepted: false,
root_password: String::new(),
root_password_confirm: String::new(),
network_wifi_password: String::new(),
network_http_proxy: String::new(),
network_https_proxy: String::new(),
network_ftp_proxy: String::new(),
use_ntp: true,
manual_year: 2026,
manual_month: 5,
manual_day: 26,
manual_hour: 12,
manual_minute: 0,
encrypt_root: false,
luks_password: String::new(),
oem_mode: false,
selected_package_groups: Vec::new(),
display_manager: "sddm".to_string(),
autologin: true,
desktop_environment: {
let de = std::env::var("XDG_CURRENT_DESKTOP")
.unwrap_or_default()
.trim()
.to_lowercase();
if de.is_empty() {
"plasma".to_string()
} else {
de
}
},
branding_icons: std::collections::HashMap::new(),
branding_pisi_dark: String::new(),
branding_pisi_light: String::new(),
branding_yali_dark: String::new(),
branding_yali_light: String::new(),
branding_slides: Vec::new(),
}
}
}
// ─────────────────────────────────────────────
// WELCOME STEP (sistem gereksinimleri + dil seçimi)
// ─────────────────────────────────────────────
@@ -167,7 +285,11 @@ pub struct WelcomeStep {
impl Default for WelcomeStep {
fn default() -> Self {
Self { checks: Vec::new(), checked: false, last_language: String::new() }
Self {
checks: Vec::new(),
checked: false,
last_language: String::new(),
}
}
}
@@ -193,12 +315,54 @@ impl InstallerStep for WelcomeStep {
}
theme::section_heading(ui, &t!("welcome_title"));
ui.label(
ui.label(
egui::RichText::new(t!("welcome_description"))
.color(theme::c_text_dim())
.size(13.0),
.size(15.0),
);
ui.add_space(16.0);
ui.vertical_centered(|ui| {
let logo_uri = if state.is_dark {
&state.branding_pisi_dark
} else {
&state.branding_pisi_light
};
if !logo_uri.is_empty() {
ui.add(
egui::Image::from_uri(logo_uri.as_str())
.max_width(120.0)
.rounding(egui::Rounding::same(8.0)),
);
}
ui.add_space(8.0);
let os_name = System::name().unwrap_or_else(|| "Bilinmiyor".to_string());
ui.label(
egui::RichText::new(format!("{} : {}", t!("os"), os_name))
.strong()
.size(16.0)
.color(theme::c_text_dim()),
);
ui.add_space(4.0);
let os_version = System::os_version().unwrap_or_else(|| "Bilinmiyor".to_string());
ui.label(
egui::RichText::new(format!("{} : {}", t!("version"), os_version))
.size(14.0)
.color(theme::c_text_dim()),
);
let desktop_name = std::env::var("XDG_CURRENT_DESKTOP").unwrap_or_default();
ui.label(
egui::RichText::new(format!("{} : {}", t!("desktop"), desktop_name))
.size(14.0)
.color(theme::c_text_dim()),
);
});
ui.add_space(12.0);
// Dil seçimi (Açılır Liste / ComboBox)
@@ -232,7 +396,6 @@ impl InstallerStep for WelcomeStep {
.striped(true)
.show(ui, |ui| {
for check in &self.checks {
// Durum simgesi + renk
let (icon, color) = match check.status {
CheckStatus::Pass => ("", theme::c_success()),
CheckStatus::Warn => ("⚠️", theme::c_warning()),
@@ -249,6 +412,27 @@ impl InstallerStep for WelcomeStep {
}
});
// Rescue mode butonu
ui.add_space(10.0);
ui.horizontal(|ui| {
let rescue_available = detect_existing_linux();
if rescue_available {
ui.colored_label(
egui::Color32::from_rgb(80, 170, 80),
"🛟",
);
if ui.add(theme::secondary_button(&t!("rescue_button"))).clicked() {
state.rescue_mode = true;
}
} else {
ui.label(
egui::RichText::new(t!("rescue_not_available"))
.size(12.0)
.color(egui::Color32::from_rgb(120, 120, 140)),
);
}
});
// Hata varsa uyarı
ui.add_space(10.0);
let has_fail = self.checks.iter().any(|c| c.status == CheckStatus::Fail);
@@ -269,11 +453,9 @@ impl InstallerStep for WelcomeStep {
}
fn is_complete(&self, state: &GlobalState) -> bool {
// Demo modunda her zaman ileri gitmeye izin ver
if state.demo_mode {
return true;
}
// Fail varsa ilerleyemez; kontroller bitmemişse de bekle
!self.checks.is_empty() && all_passed(&self.checks)
}
}
@@ -283,8 +465,9 @@ impl InstallerStep for WelcomeStep {
// ─────────────────────────────────────────────
pub struct LocationStep {
search_query: String,
timezones: Vec<String>,
geoip_done: bool,
datetime_init_done: bool,
}
impl Default for LocationStep {
@@ -296,29 +479,210 @@ impl Default for LocationStep {
"Europe/London", "Europe/Moscow", "Europe/Paris", "Europe/Rome",
"Pacific/Auckland",
].iter().map(|s| s.to_string()).collect();
Self { search_query: String::new(), timezones: tz_list }
Self {
timezones: tz_list,
geoip_done: false,
datetime_init_done: false,
}
}
}
impl LocationStep {
fn geoip_lookup(state: &mut GlobalState) {
let out = std::process::Command::new("curl")
.args(["-s", "--connect-timeout", "3", "http://ip-api.com/json"])
.output();
if let Ok(o) = out {
if let Ok(text) = String::from_utf8(o.stdout) {
let extract = |key: &str| -> Option<String> {
let pat = format!("\"{}\":\"", key);
text.find(&pat).and_then(|pos| {
let start = pos + pat.len();
text[start..].find('"').map(|end| text[start..start+end].to_string())
})
};
if let Some(country) = extract("countryCode") {
match country.as_str() {
"TR" => state.language = "tr".to_string(),
"DE" => state.language = "de".to_string(),
"FR" => state.language = "fr".to_string(),
"IT" => state.language = "it".to_string(),
"ES" => state.language = "es".to_string(),
"RU" => state.language = "ru".to_string(),
"NL" => state.language = "nl".to_string(),
"PL" => state.language = "pl".to_string(),
_ => {}
}
}
if let Some(tz) = extract("timezone") {
if !tz.is_empty() {
state.timezone = tz;
}
}
}
}
}
fn init_from_system(&mut self, state: &mut GlobalState) {
use chrono::{Datelike, Timelike};
let now_str = chrono::Local::now().format("%Y-%m-%d %H:%M").to_string();
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(&now_str, "%Y-%m-%d %H:%M") {
state.manual_year = dt.year();
state.manual_month = dt.month();
state.manual_day = dt.day();
state.manual_hour = dt.hour();
state.manual_minute = dt.minute();
}
self.datetime_init_done = true;
}
}
impl InstallerStep for LocationStep {
fn name(&self) -> String { t!("location").to_string() }
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.heading(t!("location_title"));
ui.label(t!("location_description"));
ui.add_space(10.0);
ui.label(t!("search_timezone"));
ui.text_edit_singleline(&mut self.search_query);
ui.add_space(5.0);
egui::ScrollArea::vertical().max_height(300.0).show(ui, |ui| {
for tz in &self.timezones {
if tz.to_lowercase().contains(&self.search_query.to_lowercase()) {
ui.selectable_value(&mut state.timezone, tz.clone(), tz);
}
}
});
fn name(&self) -> String {
format!("{} / {}", t!("location"), t!("datetime"))
}
fn is_complete(&self, state: &GlobalState) -> bool { !state.timezone.is_empty() }
fn on_enter(&mut self, state: &mut GlobalState) {
if !self.geoip_done {
Self::geoip_lookup(state);
self.geoip_done = true;
}
if !self.datetime_init_done {
self.init_from_system(state);
}
}
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
use crate::ui::theme;
// --- BÖLGE VE ZAMAN DİLİMİ (Region & Timezone) ---
theme::section_heading(ui, &t!("location_title"));
ui.label(
egui::RichText::new(t!("location_description"))
.color(theme::c_text_dim())
.size(13.0),
);
ui.add_space(8.0);
ui.horizontal(|ui| {
ui.label(egui::RichText::new(t!("location_title")).strong().color(theme::c_text()));
let current_tz = if state.timezone.is_empty() {
"Europe/Istanbul".to_string()
} else {
state.timezone.clone()
};
if state.timezone.is_empty() {
state.timezone = current_tz.clone();
}
egui::ComboBox::from_id_source("timezone_select")
.selected_text(&current_tz)
.width(280.0)
.show_ui(ui, |ui| {
for tz in &self.timezones {
ui.selectable_value(&mut state.timezone, tz.clone(), tz);
}
});
});
ui.add_space(16.0);
ui.separator();
ui.add_space(16.0);
// --- TARİH VE SAAT AYARLARI (Date & Time) ---
theme::section_heading(ui, &t!("datetime_title"));
ui.label(
egui::RichText::new(t!("datetime_description"))
.color(theme::c_text_dim())
.size(13.0),
);
ui.add_space(8.0);
egui::Frame::group(ui.style())
.fill(theme::c_bg_widget())
.rounding(6.0)
.inner_margin(egui::Margin::same(12.0))
.show(ui, |ui| {
ui.set_width(ui.available_width());
egui::Grid::new("datetime_grid")
.num_columns(2)
.spacing([12.0, 10.0])
.show(ui, |ui| {
// NTP
ui.label(egui::RichText::new(t!("datetime_ntp_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
ui.add(egui::Checkbox::new(&mut state.use_ntp, ""));
let status = if state.use_ntp { t!("datetime_ntp_on") } else { t!("datetime_ntp_off") };
ui.colored_label(
if state.use_ntp { theme::c_success() } else { theme::c_error() },
status,
);
});
ui.end_row();
if !state.use_ntp {
// Tarih
ui.label(egui::RichText::new(t!("datetime_date_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
ui.label(t!("datetime_year"));
ui.add(egui::DragValue::new(&mut state.manual_year).clamp_range(2000..=2100).speed(1));
ui.label(t!("datetime_month"));
ui.add(egui::DragValue::new(&mut state.manual_month).clamp_range(1..=12).speed(1));
ui.label(t!("datetime_day"));
ui.add(egui::DragValue::new(&mut state.manual_day).clamp_range(1..=31).speed(1));
});
ui.end_row();
// Saat
ui.label(egui::RichText::new(t!("datetime_time_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
ui.add(egui::DragValue::new(&mut state.manual_hour).clamp_range(0..=23).speed(1));
ui.label(":");
ui.add(egui::DragValue::new(&mut state.manual_minute).clamp_range(0..=59).speed(1));
});
ui.end_row();
}
});
});
ui.add_space(8.0);
if state.use_ntp {
ui.label(
egui::RichText::new(t!("datetime_ntp_info"))
.size(11.0)
.color(theme::c_text_dim()),
);
} else {
ui.label(
egui::RichText::new(t!("datetime_manual_info"))
.size(11.0)
.color(theme::c_text_dim()),
);
}
}
fn is_complete(&self, state: &GlobalState) -> bool {
!state.timezone.is_empty()
}
}
/// Mevcut bir Linux kurulumu olup olmadığını kontrol eder.
/// Basitçe /mnt/etc/fstab varlığına veya blkid ile Linux bölümlerine bakar.
fn detect_existing_linux() -> bool {
// /etc/fstab veya /mnt/etc/fstab kontrolü
if std::path::Path::new("/mnt/etc/fstab").exists() {
return true;
}
// blkid ile Linux bölümlerini tara
if let Ok(out) = std::process::Command::new("blkid")
.args(["-o", "list"])
.output()
{
let text = String::from_utf8_lossy(&out.stdout);
text.lines().any(|l| l.contains("ext4") || l.contains("btrfs") || l.contains("xfs"))
} else {
false
}
}
+971 -205
View File
File diff suppressed because it is too large Load Diff
+140 -59
View File
@@ -3,9 +3,10 @@ mod steps;
mod jobs;
mod ui;
mod autoinstall;
mod branding;
use installer::{GlobalState, InstallerStep, WelcomeStep, LocationStep};
use steps::{KeyboardStep, UsersStep, SummaryStep, ExecutionStep, FinishStep, PartitionStep};
use steps::{KeyboardStep, UsersStep, SummaryStep, ExecutionStep, FinishStep, PartitionStep, BootloaderStep, LicenseStep, NetworkStep, DisplayManagerStep};
use eframe::egui;
use rust_i18n::t;
@@ -32,14 +33,14 @@ fn main() -> eframe::Result<()> {
fn run_gui(demo_mode: bool) -> eframe::Result<()> {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([1100.0, 760.0])
.with_min_inner_size([900.0, 650.0])
.with_title(t!("installer").to_string()),
.with_inner_size([1200.0, 900.0])
.with_min_inner_size([1080.0, 720.0])
.with_title(t!("installer")),
..Default::default()
};
eframe::run_native(
&t!("installer").to_string(),
t!("installer").as_ref(),
options,
Box::new(move |cc| {
// Görsel yükleyicileri etkinleştir (PNG desteği)
@@ -88,17 +89,19 @@ fn run_auto_install(path: &str, demo_mode: bool) -> eframe::Result<()> {
}
if !autoinstall::checker::all_passed(&checks) {
eprintln!("{}: {}", t!("error"), "Critical check error. Installation stopped.");
eprintln!("{}: Critical check error. Installation stopped.", t!("error"));
std::process::exit(2);
}
// State'i hazırla
let mut state = GlobalState::default();
state.demo_mode = demo_mode;
let mut state = GlobalState {
demo_mode,
..Default::default()
};
apply_to_state(&af, &mut state);
if state.partition_plan.is_none() {
eprintln!("{}: {}", t!("error"), format!("{}: {}", t!("partition_plan_could_not_be_created"), af.install.disk));
eprintln!("{}: {}: {}", t!("error"), t!("partition_plan_could_not_be_created"), af.install.disk);
std::process::exit(3);
}
@@ -122,13 +125,9 @@ fn run_auto_install(path: &str, demo_mode: bool) -> eframe::Result<()> {
use jobs::{InstallMessage, UiSender};
// CLI modunda egui Context yok; basit bir mpsc kanal kullanıyoruz.
// UiSender'a dummy bir context gerekeceğinden terminal çıktısı için
// doğrudan kanalı dinliyoruz.
let (tx, rx) = mpsc::channel::<InstallMessage>();
// Arka plan iş parçacığı — gerçek job kuyruğunu çalıştırır.
// egui::Context::default() CLI modunda request_repaint()'i no-op yapar;
// mesajlar mpsc kanalı üzerinden terminale aktarılır.
let cli_ctx = eframe::egui::Context::default();
let ui_sender = UiSender::new(tx, cli_ctx);
@@ -147,16 +146,33 @@ fn run_auto_install(path: &str, demo_mode: bool) -> eframe::Result<()> {
let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
let mut queue = jobs::build_full_job_queue(
plan_c,
&mount_c,
&source_c,
&locale_c,
&timezone_c,
&username_c,
&password_c,
&hostname_c,
&kb_layout_c,
&kb_variant_c,
demo_mode,
jobs::JobQueueConfig {
mount: mount_c,
source: source_c,
locale: locale_c,
timezone: timezone_c,
username: username_c,
password: password_c,
hostname: hostname_c,
kb_layout: kb_layout_c,
kb_variant: kb_variant_c,
demo_mode,
boot_device: String::new(),
bootloader_timeout: 5,
bootloader_password: String::new(),
kernel_options: String::new(),
use_ntp: true,
root_password: String::new(),
display_manager: "sddm".to_string(),
autologin: true,
desktop_environment: "plasma".to_string(),
selected_package_groups: Vec::new(),
manual_year: 2026,
manual_month: 5,
manual_day: 26,
manual_hour: 12,
manual_minute: 0,
},
);
rt.block_on(queue.run_all(ui_sender));
});
@@ -182,32 +198,69 @@ fn run_auto_install(path: &str, demo_mode: bool) -> eframe::Result<()> {
}
});
// eframe::Result döndürmek için Ok
Ok(())
}
struct YaliApp {
state: GlobalState,
steps: Vec<Box<dyn InstallerStep>>,
last_step: usize,
state: GlobalState,
steps: Vec<Box<dyn InstallerStep>>,
rescue_step: Option<Box<dyn InstallerStep>>,
last_step: usize,
branding: branding::BrandingConfig,
loaded_language: String,
}
impl Default for YaliApp {
fn default() -> Self {
let default_lang = "tr".to_string();
let branding = branding::BrandingConfig::load_for_lang(&default_lang);
let mut app = Self {
state: GlobalState::default(),
steps: vec![
Box::new(WelcomeStep::default()),
Box::new(LocationStep::default()),
Box::new(LicenseStep),
Box::new(KeyboardStep::default()),
Box::new(NetworkStep::default()),
Box::new(PartitionStep::default()),
Box::new(UsersStep::default()),
Box::new(BootloaderStep::default()),
Box::new(DisplayManagerStep::default()),
Box::new(SummaryStep),
Box::new(ExecutionStep::default()),
Box::new(FinishStep),
],
rescue_step: Some(Box::new(steps::RescueStep::default())),
last_step: usize::MAX,
branding: branding.clone(),
loaded_language: String::new(),
};
// OEM modu: varsayılan değerleri ata ve step engelleme
if branding.oem.enabled {
app.state.oem_mode = true;
if !branding.oem.default_username.is_empty() {
app.state.username = branding.oem.default_username.clone();
app.state.hostname = branding.oem.default_hostname.clone();
}
if !branding.oem.default_password.is_empty() {
app.state.password = branding.oem.default_password.clone();
app.state.password_confirm = branding.oem.default_password.clone();
}
// OEM ön tanımlı disk planı varsa?
}
// Branding görsel verilerini GlobalState'e enjekte et
app.state.branding_yali_dark = branding.yali_logo_uri();
app.state.branding_yali_light = branding.yali_logo_uri();
app.state.branding_pisi_dark = branding.pisi_logo_dark_uri();
app.state.branding_pisi_light = branding.pisi_logo_light_uri();
app.state.branding_icons = branding.slides.icons
.iter()
.map(|(k, v)| (k.clone(), branding::path_to_file_uri(v)))
.collect();
app.state.branding_slides = branding.slides.items.clone();
app.steps[0].on_enter(&mut app.state);
app.last_step = 0;
app
@@ -218,6 +271,45 @@ impl eframe::App for YaliApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
rust_i18n::set_locale(&self.state.language);
// Dil değişimi kontrolü -> branding dosyasını dinamik olarak yeniden yükle
if self.loaded_language != self.state.language {
self.loaded_language = self.state.language.clone();
let branding = branding::BrandingConfig::load_for_lang(&self.loaded_language);
self.branding = branding.clone();
self.state.branding_yali_dark = branding.yali_logo_uri();
self.state.branding_yali_light = branding.yali_logo_uri();
self.state.branding_pisi_dark = branding.pisi_logo_dark_uri();
self.state.branding_pisi_light = branding.pisi_logo_light_uri();
self.state.branding_icons = branding.slides.icons
.iter()
.map(|(k, v)| (k.clone(), branding::path_to_file_uri(v)))
.collect();
self.state.branding_slides = branding.slides.items.clone();
// Pencere başlığını dinamik olarak güncelle
ctx.send_viewport_cmd(egui::ViewportCommand::Title(self.branding.general.title.clone()));
}
// Rescue modu: tam ekran, sidebar yok, navigasyon yok
if self.state.rescue_mode {
if let Some(rescue) = &mut self.rescue_step {
egui::CentralPanel::default()
.frame(egui::Frame::none()
.fill(ui::theme::c_bg_dark())
.inner_margin(egui::Margin::same(20.0)))
.show(ctx, |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
rescue.show(ui, &mut self.state);
});
ui.add_space(12.0);
if ui.button(t!("rescue_return")).clicked() {
self.state.rescue_mode = false;
}
});
}
return;
}
let cur = self.state.current_step;
let n_steps = self.steps.len();
let is_exec = cur == n_steps - 2;
@@ -238,31 +330,31 @@ impl eframe::App for YaliApp {
.show(ctx, |ui| {
ui.add_space(20.0);
// PisiLinux logo
// Dağıtım logosu — branding.toml'dan dinamik yüklenir
ui.vertical_centered(|ui| {
let logo = if self.state.is_dark {
egui::include_image!("../assets/pisi-logo-dark.png")
let logo_uri = if self.state.is_dark {
&self.state.branding_yali_dark
} else {
egui::include_image!("../assets/pisi-logo-light.png")
&self.state.branding_yali_light
};
ui.add(
egui::Image::new(logo)
.max_width(120.0)
.rounding(egui::Rounding::same(8.0)),
);
if !logo_uri.is_empty() {
ui.add(
egui::Image::from_uri(logo_uri.as_str())
.max_width(120.0)
.rounding(egui::Rounding::same(8.0)),
);
}
ui.add_space(8.0);
ui.label(
egui::RichText::new(t!("os_name"))
egui::RichText::new(t!("app_name"))
.strong()
.size(16.0)
.color(ui::theme::c_text_dim()),
);
// get desktop environment
let desktop = std::env::var("XDG_CURRENT_DESKTOP").unwrap_or_default();
ui.label(
egui::RichText::new(&format!("{} Desktop", desktop))
.size(14.0)
.color(ui::theme::c_text_dim()),
ui.colored_label(
egui::Color32::from_rgb(60, 60, 90),
format!("{}: {}", t!("version"), env!("CARGO_PKG_VERSION")),
);
});
@@ -305,15 +397,10 @@ impl eframe::App for YaliApp {
ui.add_space(2.0);
}
// Alt kısım: tema değiştirme + sürüm bilgisi
// Alt kısım: tema değiştirme
ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
ui.add_space(12.0);
ui.colored_label(
egui::Color32::from_rgb(60, 60, 90),
format!("YALI (v{})", env!("CARGO_PKG_VERSION")),
);
ui.add_space(6.0);
// Tema değiştirme butonu
let theme_icon = if self.state.is_dark { "" } else { "🌙" };
let theme_label = if self.state.is_dark { "Light" } else { "Dark" };
if ui.add(
@@ -341,11 +428,8 @@ impl eframe::App for YaliApp {
ui.horizontal(|ui| {
ui.add_space(8.0);
// İptal — kurulum ve bitiş ekranında gizle
if !is_exec && !is_finish {
if ui.add(ui::theme::secondary_button(&t!("cancel"))).clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
if !is_exec && !is_finish && ui.add(ui::theme::secondary_button(&t!("cancel"))).clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
@@ -357,7 +441,6 @@ impl eframe::App for YaliApp {
.unwrap_or(false);
if !is_finish {
// İleri / Tamamlandı butonu
if !is_exec || is_valid {
let next_label = if is_exec {
t!("finish_next")
@@ -367,7 +450,6 @@ impl eframe::App for YaliApp {
let btn = if is_valid {
ui::theme::primary_button(&next_label)
} else {
// Devre dışı görünüm için secondary kullan
ui::theme::secondary_button(&next_label)
};
if ui.add_enabled(is_valid, btn).clicked() {
@@ -375,7 +457,6 @@ impl eframe::App for YaliApp {
}
}
// Geri — kurulum ekranında gizle
if cur > 0 && !is_exec {
ui.add_space(8.0);
if ui.add(ui::theme::secondary_button(&t!("back"))).clicked() {
@@ -403,4 +484,4 @@ impl eframe::App for YaliApp {
});
});
}
}
}
+111
View File
@@ -0,0 +1,111 @@
use eframe::egui;
use rust_i18n::t;
use crate::installer::{GlobalState, InstallerStep};
#[derive(Default)]
pub struct BootloaderStep {
show_password: bool,
timeout_str: String,
}
impl InstallerStep for BootloaderStep {
fn name(&self) -> String {
t!("bootloader").to_string()
}
fn on_enter(&mut self, state: &mut GlobalState) {
if self.timeout_str.is_empty() {
self.timeout_str = state.bootloader_timeout.to_string();
}
if state.bootloader_device.is_empty() {
if let Some(ref disk) = state.selected_disk {
state.bootloader_device = disk.clone();
}
}
}
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.heading(t!("bootloader_title"));
ui.label(t!("bootloader_description"));
ui.add_space(12.0);
egui::Grid::new("bootloader_grid")
.num_columns(2)
.spacing([12.0, 10.0])
.show(ui, |ui| {
// Hedef aygıt
ui.label(t!("bootloader_device_label"));
ui.horizontal(|ui| {
let mut device_list: Vec<String> = state.available_disks.iter()
.map(|d| d.name.clone())
.collect();
if device_list.is_empty() {
device_list.push(state.bootloader_device.clone());
}
let current = if state.bootloader_device.is_empty() {
device_list[0].clone()
} else {
state.bootloader_device.clone()
};
egui::ComboBox::from_id_source("boot_device_select")
.selected_text(&current)
.show_ui(ui, |ui| {
for dev in &device_list {
ui.selectable_value(
&mut state.bootloader_device,
dev.clone(),
dev,
);
}
});
ui.label(
egui::RichText::new(t!("bootloader_device_hint"))
.size(11.0)
.color(egui::Color32::from_rgb(120, 120, 140)),
);
});
ui.end_row();
// Timeout
ui.label(t!("bootloader_timeout_label"));
ui.horizontal(|ui| {
ui.add(egui::Slider::new(&mut state.bootloader_timeout, 0..=60)
.text(t!("bootloader_timeout_suffix")));
});
ui.end_row();
// GRUB şifresi
ui.label(t!("bootloader_password_label"));
ui.horizontal(|ui| {
if self.show_password {
ui.text_edit_singleline(&mut state.bootloader_password);
} else {
ui.add(egui::TextEdit::singleline(&mut state.bootloader_password).password(true));
}
if ui.small_button(if self.show_password { "🙈" } else { "👁" }).clicked() {
self.show_password = !self.show_password;
}
if !state.bootloader_password.is_empty() {
ui.colored_label(egui::Color32::from_rgb(200, 180, 40), "");
}
});
ui.end_row();
// Kernel parametreleri
ui.label(t!("bootloader_kernel_label"));
ui.horizontal(|ui| {
ui.text_edit_singleline(&mut state.kernel_options);
ui.label(
egui::RichText::new(t!("bootloader_kernel_hint"))
.size(11.0)
.color(egui::Color32::from_rgb(120, 120, 140)),
);
});
ui.end_row();
});
}
fn is_complete(&self, state: &GlobalState) -> bool {
!state.bootloader_device.is_empty()
}
}
+86
View File
@@ -0,0 +1,86 @@
use eframe::egui;
use rust_i18n::t;
use crate::installer::{GlobalState, InstallerStep};
const DM_OPTIONS: &[&str] = &["sddm", "lightdm", "gdm"];
#[derive(Default)]
pub struct DisplayManagerStep;
impl InstallerStep for DisplayManagerStep {
fn name(&self) -> String {
t!("display_manager").to_string()
}
fn on_enter(&mut self, state: &mut GlobalState) {
let desktop = std::env::var("XDG_CURRENT_DESKTOP")
.unwrap_or_default()
.trim()
.to_lowercase();
if !desktop.is_empty() {
state.desktop_environment = desktop;
} else if state.desktop_environment.is_empty() {
state.desktop_environment = "plasma".to_string();
}
}
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.heading(t!("display_manager_title"));
ui.label(t!("display_manager_description"));
ui.add_space(12.0);
egui::Grid::new("dm_grid")
.num_columns(2)
.spacing([12.0, 10.0])
.show(ui, |ui| {
// Ekran yöneticisi
ui.label(t!("display_manager_select"));
egui::ComboBox::from_id_source("dm_combo")
.selected_text(state.display_manager.as_str())
.show_ui(ui, |ui| {
for dm in DM_OPTIONS {
ui.selectable_value(
&mut state.display_manager,
dm.to_string(),
*dm,
);
}
});
ui.end_row();
// Masaüstü ortamı (otomatik algılanır)
ui.label(t!("display_manager_de"));
if !state.desktop_environment.is_empty() && state.desktop_environment != "default" {
ui.colored_label(egui::Color32::from_rgb(120, 180, 120), &state.desktop_environment);
} else {
ui.colored_label(egui::Color32::from_rgb(200, 160, 40), t!("display_manager_de_not_found"));
}
ui.end_row();
// Otomatik oturum açma
ui.label(t!("display_manager_autologin"));
ui.checkbox(&mut state.autologin, "");
ui.end_row();
if state.autologin {
ui.label(t!("display_manager_autologin_user"));
ui.horizontal(|ui| {
let name = if !state.username.is_empty() {
state.username.clone()
} else {
t!("display_manager_autologin_hint").to_string()
};
ui.colored_label(egui::Color32::from_rgb(120, 180, 120), name);
});
ui.end_row();
}
});
ui.add_space(8.0);
ui.label(egui::RichText::new(t!("display_manager_note")).size(11.0).color(egui::Color32::from_rgb(140, 140, 160)));
}
fn is_complete(&self, _state: &GlobalState) -> bool {
true
}
}
+80 -26
View File
@@ -15,6 +15,7 @@ use crate::jobs::{self, InstallMessage, UiSender};
use crate::ui::{ErrorScreen, Slideshow};
use crate::ui::theme;
#[derive(Default)]
pub struct ExecutionStep {
rx: Option<mpsc::Receiver<InstallMessage>>,
tx: Option<mpsc::Sender<InstallMessage>>,
@@ -23,18 +24,6 @@ pub struct ExecutionStep {
slideshow: Option<Slideshow>,
}
impl Default for ExecutionStep {
fn default() -> Self {
Self {
rx: None,
tx: None,
finished: false,
error_screen: None,
slideshow: None,
}
}
}
impl InstallerStep for ExecutionStep {
fn name(&self) -> String { t!("execution").to_string() }
@@ -48,7 +37,7 @@ impl InstallerStep for ExecutionStep {
let desktop = std::env::var("XDG_CURRENT_DESKTOP")
.unwrap_or_else(|_| "default".to_string())
.to_lowercase();
self.slideshow = Some(Slideshow::new(&desktop));
self.slideshow = Some(Slideshow::new(&desktop, state.branding_icons.clone(), state.branding_slides.clone()));
state.install_progress = 0.0;
state.install_log.clear();
@@ -70,8 +59,26 @@ impl InstallerStep for ExecutionStep {
let ctx = ui.ctx().clone();
let erase_disk = state.erase_disk;
let custom_partitions = state.custom_partitions.clone();
let volume_groups = state.volume_groups.clone();
let logical_volumes = state.logical_volumes.clone();
let selected_disk = state.selected_disk.clone();
let boot_device = state.bootloader_device.clone();
let bootloader_timeout = state.bootloader_timeout;
let bootloader_password = state.bootloader_password.clone();
let kernel_options = state.kernel_options.clone();
let use_ntp = state.use_ntp;
let root_password = state.root_password.clone();
let display_manager = state.display_manager.clone();
let autologin = state.autologin;
let desktop_environment = state.desktop_environment.clone();
let selected_package_groups = state.selected_package_groups.clone();
let manual_year = state.manual_year;
let manual_month = state.manual_month;
let manual_day = state.manual_day;
let manual_hour = state.manual_hour;
let manual_minute = state.manual_minute;
let ui_sender = UiSender::new(tx, ctx);
thread::spawn(move || {
@@ -86,11 +93,34 @@ impl InstallerStep for ExecutionStep {
if erase_disk {
if let Some(p) = plan {
let mut queue = jobs::build_full_job_queue(
p, "/mnt", &src,
&locale, &timezone,
&username, &password, &hostname,
&kb_layout, &kb_variant,
demo_mode,
p,
jobs::JobQueueConfig {
mount: "/mnt".to_string(),
source: src,
locale,
timezone,
username,
password,
hostname,
kb_layout,
kb_variant,
demo_mode,
boot_device,
bootloader_timeout,
bootloader_password,
kernel_options,
use_ntp,
root_password,
display_manager: display_manager.clone(),
autologin,
desktop_environment: desktop_environment.clone(),
selected_package_groups: selected_package_groups.clone(),
manual_year,
manual_month,
manual_day,
manual_hour,
manual_minute,
},
);
rt.block_on(queue.run_all(ui_sender));
} else {
@@ -98,14 +128,39 @@ impl InstallerStep for ExecutionStep {
t!("error_partition_plan_not_found").to_string()
));
}
} else {
if let Some(disk) = selected_disk {
} else if let Some(disk) = selected_disk {
let mut queue = jobs::build_custom_job_queue(
disk, custom_partitions, "/mnt", &src,
&locale, &timezone,
&username, &password, &hostname,
&kb_layout, &kb_variant,
demo_mode,
disk,
custom_partitions,
volume_groups,
logical_volumes,
jobs::JobQueueConfig {
mount: "/mnt".to_string(),
source: src,
locale,
timezone,
username,
password,
hostname,
kb_layout,
kb_variant,
demo_mode,
boot_device,
bootloader_timeout,
bootloader_password,
kernel_options,
use_ntp,
root_password,
display_manager: display_manager.clone(),
autologin,
desktop_environment: desktop_environment.clone(),
selected_package_groups: selected_package_groups.clone(),
manual_year,
manual_month,
manual_day,
manual_hour,
manual_minute,
},
);
rt.block_on(queue.run_all(ui_sender));
} else {
@@ -113,7 +168,6 @@ impl InstallerStep for ExecutionStep {
t!("error_target_disk_not_selected").to_string()
));
}
}
});
}
+52
View File
@@ -0,0 +1,52 @@
use eframe::egui;
use rust_i18n::t;
use crate::installer::{GlobalState, InstallerStep};
#[derive(Default)]
pub struct LicenseStep;
const GPL_TEXT: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/LICENSE"));
impl InstallerStep for LicenseStep {
fn name(&self) -> String {
t!("license").to_string()
}
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.heading(t!("license_title"));
ui.label(t!("license_description"));
ui.add_space(8.0);
let avail = ui.available_height();
egui::ScrollArea::vertical()
.max_height(avail - 60.0)
.stick_to_bottom(true)
.auto_shrink([false; 2])
.show(ui, |ui| {
egui::Frame::group(ui.style())
.inner_margin(egui::Margin::same(12.0))
.show(ui, |ui| {
ui.label(
egui::RichText::new(GPL_TEXT)
.size(14.0)
.family(egui::FontFamily::Monospace)
.color(egui::Color32::from_rgb(180, 180, 200)),
);
});
});
ui.add_space(10.0);
ui.horizontal(|ui| {
ui.add(egui::Checkbox::new(&mut state.license_accepted, ""));
ui.label(
egui::RichText::new(t!("license_accept"))
.size(14.0)
.strong(),
);
});
}
fn is_complete(&self, state: &GlobalState) -> bool {
state.license_accepted
}
}
+11
View File
@@ -4,6 +4,12 @@ pub mod summary;
pub mod execution;
pub mod finish;
pub mod partition;
pub mod bootloader;
pub mod license;
pub mod network;
pub mod rescue;
pub mod display_manager;
pub mod netinstall;
pub use keyboard::KeyboardStep;
pub use users::UsersStep;
@@ -11,3 +17,8 @@ pub use summary::SummaryStep;
pub use execution::ExecutionStep;
pub use finish::FinishStep;
pub use partition::PartitionStep;
pub use bootloader::BootloaderStep;
pub use license::LicenseStep;
pub use network::NetworkStep;
pub use rescue::RescueStep;
pub use display_manager::DisplayManagerStep;
+68
View File
@@ -0,0 +1,68 @@
use eframe::egui;
use rust_i18n::t;
use crate::installer::{GlobalState, InstallerStep};
pub const PACKAGE_GROUPS: &[(&str, &str, &[&str])] = &[
("office", "Office Suite", &["libreoffice", "calligra"]),
("multimedia", "Multimedia", &["vlc", "audacity", "gimp", "inkscape"]),
("development", "Development Tools",&["gcc", "make", "cmake", "git", "python3", "rust"]),
("network", "Network Tools", &["firefox", "chromium", "thunderbird", "filezilla"]),
("graphics", "Graphics & Design",&["blender", "krita", "darktable"]),
("games", "Games", &["steam", "lutris", "supertuxkart"]),
("vm", "Virtualization", &["virtualbox", "qemu", "docker"]),
("server", "Server", &["nginx", "apache", "mariadb", "postgresql"]),
];
#[derive(Default)]
#[allow(dead_code)]
pub struct NetinstallStep;
impl InstallerStep for NetinstallStep {
fn name(&self) -> String {
t!("netinstall").to_string()
}
fn on_enter(&mut self, state: &mut GlobalState) {
if state.selected_package_groups.is_empty() {
// Varsayılan ofis ve network
state.selected_package_groups = vec!["office".to_string(), "network".to_string()];
}
}
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.heading(t!("netinstall_title"));
ui.label(t!("netinstall_description"));
ui.add_space(10.0);
let screen_width = ui.available_width();
let cols = if screen_width > 700.0 { 3 } else if screen_width > 500.0 { 2 } else { 1 };
egui::Grid::new("netinstall_grid")
.num_columns(cols)
.spacing([8.0, 4.0])
.show(ui, |ui| {
for (key, label, _pkgs) in PACKAGE_GROUPS {
let mut selected = state.selected_package_groups.contains(&key.to_string());
ui.checkbox(&mut selected, *label);
if selected && !state.selected_package_groups.contains(&key.to_string()) {
state.selected_package_groups.push(key.to_string());
} else if !selected {
state.selected_package_groups.retain(|k| k != key);
}
}
});
ui.add_space(12.0);
if !state.selected_package_groups.is_empty() {
let total: usize = PACKAGE_GROUPS.iter()
.filter(|(k, _, _)| state.selected_package_groups.contains(&k.to_string()))
.map(|(_, _, pkgs)| pkgs.len())
.sum();
ui.label(format!("{} {} {}", total, t!("netinstall_package_count"), t!("netinstall_selected")));
}
}
fn is_complete(&self, _state: &GlobalState) -> bool {
true
}
}
+205
View File
@@ -0,0 +1,205 @@
use eframe::egui;
use rust_i18n::t;
use crate::installer::{GlobalState, InstallerStep};
#[derive(Default)]
pub struct NetworkStep {
scan_results: Vec<WifiNetwork>,
show_password: Vec<bool>,
scanning: bool,
scan_done: bool,
proxy_enabled: bool,
}
#[derive(Clone, Default)]
struct WifiNetwork {
ssid: String,
signal: String,
secured: bool,
active: bool,
}
impl NetworkStep {
fn scan_wifi(&mut self) {
self.scanning = true;
self.scan_done = false;
self.scan_results.clear();
if let Ok(out) = std::process::Command::new("nmcli")
.args(["-t", "-f", "SSID,SIGNAL,SECURITY,ACTIVE", "dev", "wifi", "list"])
.output()
{
if out.status.success() {
let text = String::from_utf8_lossy(&out.stdout);
for line in text.lines() {
let parts: Vec<&str> = line.split(':').collect();
if parts.len() >= 3 {
let ssid = parts[0].trim().to_string();
if ssid.is_empty() || ssid == "--" { continue; }
self.scan_results.push(WifiNetwork {
ssid,
signal: parts[1].to_string(),
secured: !parts[2].is_empty() && parts[2] != "(none)" && parts[2] != "--",
active: parts.get(3).map(|s| s.trim() == "yes").unwrap_or(false),
});
}
}
}
}
self.show_password = vec![false; self.scan_results.len()];
self.scanning = false;
self.scan_done = true;
}
fn connect_wifi(ssid: &str, password: &str) -> Result<String, String> {
let mut args = vec!["dev", "wifi", "connect", ssid];
if !password.is_empty() {
args.extend_from_slice(&["password", password]);
}
let out = std::process::Command::new("nmcli")
.args(&args)
.output()
.map_err(|e| format!("nmcli error: {}", e))?;
if out.status.success() {
Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
} else {
Err(String::from_utf8_lossy(&out.stderr).trim().to_string())
}
}
}
impl InstallerStep for NetworkStep {
fn name(&self) -> String {
t!("network").to_string()
}
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.heading(t!("network_title"));
ui.label(t!("network_description"));
ui.add_space(12.0);
// Mevcut bağlantı durumu
if let Ok(out) = std::process::Command::new("nmcli")
.args(["-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "dev", "status"])
.output()
{
let text = String::from_utf8_lossy(&out.stdout);
let connected: Vec<&str> = text.lines()
.filter(|l| l.contains(":connected:") || l.contains(":connecting:"))
.collect();
if !connected.is_empty() {
ui.colored_label(
egui::Color32::from_rgb(80, 170, 80),
format!("{} {}", t!("network_connected"), connected.join(", ")),
);
} else {
ui.colored_label(
egui::Color32::from_rgb(200, 140, 40),
t!("network_disconnected"),
);
}
}
ui.add_space(8.0);
// Proxy ayarları
ui.horizontal(|ui| {
ui.add(egui::Checkbox::new(&mut self.proxy_enabled, ""));
ui.label(t!("network_proxy_label"));
});
if self.proxy_enabled {
egui::Grid::new("proxy_grid")
.num_columns(2)
.spacing([12.0, 6.0])
.show(ui, |ui| {
ui.label("HTTP:");
ui.text_edit_singleline(&mut state.network_http_proxy);
ui.end_row();
ui.label("HTTPS:");
ui.text_edit_singleline(&mut state.network_https_proxy);
ui.end_row();
ui.label("FTP:");
ui.text_edit_singleline(&mut state.network_ftp_proxy);
ui.end_row();
});
}
ui.add_space(12.0);
// Wi-Fi tara
if !self.scan_done {
if ui.add(egui::Button::new(t!("network_scan"))).clicked() {
self.scan_wifi();
}
} else {
ui.horizontal(|ui| {
if ui.add(egui::Button::new(t!("network_rescan"))).clicked() {
self.scan_done = false;
}
ui.label(
egui::RichText::new(format!("{}: {}", t!("network_scan_count"), self.scan_results.len()))
.size(12.0)
.color(egui::Color32::from_rgb(120, 120, 140)),
);
});
}
ui.add_space(6.0);
// Wi-Fi listesi
if !self.scan_results.is_empty() {
egui::ScrollArea::vertical()
.max_height(300.0)
.show(ui, |ui| {
egui::Frame::group(ui.style())
.inner_margin(egui::Margin::same(6.0))
.show(ui, |ui| {
for (i, net) in self.scan_results.clone().iter().enumerate() {
ui.horizontal(|ui| {
let lock = if net.secured { "🔒" } else { "🌐" };
let sig = net.signal.parse::<i32>().unwrap_or(0);
let bars = if sig > -50 { "▂▄▆█" } else if sig > -67 { "▂▄▆" } else if sig > -80 { "▂▄" } else { "" };
let active = if net.active { "" } else { "" };
ui.label(format!("{} {}{}", lock, net.ssid, active));
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
ui.label(
egui::RichText::new(bars)
.size(10.0)
.color(egui::Color32::from_rgb(120, 120, 140)),
);
if net.secured && !net.active {
ui.add(egui::TextEdit::singleline(&mut state.network_wifi_password)
.password(!self.show_password.get(i).copied().unwrap_or(false))
.hint_text(t!("network_password_hint"))
.desired_width(120.0));
if ui.small_button("👁").clicked() {
if self.show_password.len() <= i {
self.show_password.resize(i + 1, false);
}
self.show_password[i] = !self.show_password[i];
}
}
if !net.active && ui.add(egui::Button::new(t!("network_connect"))).clicked() {
let pw = if net.secured { state.network_wifi_password.clone() } else { String::new() };
match Self::connect_wifi(&net.ssid, &pw) {
Ok(msg) => {
state.install_log.push(format!("Wi-Fi: {}{}", net.ssid, msg));
self.scan_done = false;
}
Err(e) => {
state.install_log.push(format!("Wi-Fi error: {}", e));
}
}
}
});
});
ui.separator();
}
});
});
}
}
fn is_complete(&self, _state: &GlobalState) -> bool {
true
}
}
+440 -36
View File
@@ -5,11 +5,84 @@ use rust_i18n::t;
// sysinfo::Disks mount point başına bir kayıt döndürür (partition'lar),
// ham disk listesi için lsblk kullanıyoruz.
// ─────────────────────────────────────────────
// Mevcut disk bölümlerini oku
// ─────────────────────────────────────────────
/// Verilen diskin (`/dev/sda`, `/dev/nvme0n1` vb.) mevcut bölümlerini
/// `lsblk` aracılığıyla okur ve `CustomPartition` listesi olarak döndürür.
///
/// Hem SATA/virtio tipi (`sda` → `sda1`, `sda2`) hem de NVMe tipi
/// (`nvme0n1` → `nvme0n1p1`, `nvme0n1p2`) aygıtları desteklenir.
/// Manuel bölümleme modunda disk seçildiğinde çağrılır; böylece kullanıcı
/// diskin mevcut bölüm düzenini görebilir.
fn read_existing_partitions(disk_path: &str) -> Vec<crate::installer::CustomPartition> {
use crate::installer::{CustomPartition, FsType};
let dev_name = disk_path.trim_start_matches("/dev/");
let out = std::process::Command::new("lsblk")
.args([
"-o", "NAME,SIZE,FSTYPE,MOUNTPOINT",
"-b", "--noheadings", "-l",
disk_path,
])
.output();
let mut parts = Vec::new();
if let Ok(o) = out {
if o.status.success() {
let text = String::from_utf8_lossy(&o.stdout);
for line in text.lines() {
let mut tokens = line.split_whitespace();
let name = match tokens.next() { Some(n) => n, None => continue };
// Diskin kendisini (örn. "sda") atla — yalnızca bölümleri al
if name == dev_name { continue; }
// Bu diske ait olmayan girişleri atla
// SATA: sda1, sda2 … NVMe: nvme0n1p1, nvme0n1p2 …
if !name.starts_with(dev_name) { continue; }
let size_bytes: u64 = tokens.next().unwrap_or("0").parse().unwrap_or(0);
let size_mb = size_bytes / (1024 * 1024);
let fstype_str = tokens.next().unwrap_or("");
let fstype = match fstype_str {
"ext4" => FsType::Ext4,
"ext3" => FsType::Ext3,
"btrfs" => FsType::Btrfs,
"xfs" => FsType::Xfs,
"vfat" => FsType::Fat32,
"ntfs" => FsType::Ntfs,
"swap" => FsType::Swap,
_ => FsType::Ext4, // bilinmeyen → varsayılan
};
// lsblk mount point alanı boşsa sonraki token gelmez; unwrap_or ile güvenli al
let mountpoint = tokens.next().unwrap_or("").to_string();
parts.push(CustomPartition {
device: format!("/dev/{}", name),
size_mb,
fstype,
mountpoint,
encrypt: false,
luks_password: String::new(),
luks_name: String::new(),
});
}
}
}
parts
}
/// `/sys/block` altındaki gerçek blok aygıtlarını (partition değil) listeler.
/// lsblk'dan JSON çıktısı ayrıştırılır; başarısız olursa /sys/block fallback.
/// lsblk'dan gelen çıktı akıllıca ayrıştırılır; başarısız olursa /sys/block fallback devreye girer.
fn list_block_devices() -> Vec<crate::installer::DiskInfo> {
// lsblk -d: sadece diskler (partition'lar hariç)
// -o NAME,SIZE,MODEL,TYPE -b: byte cinsinden boyut
let out = std::process::Command::new("lsblk")
.args(["-d", "-o", "NAME,SIZE,MODEL", "-b", "--noheadings"])
.output();
@@ -17,43 +90,64 @@ fn list_block_devices() -> Vec<crate::installer::DiskInfo> {
let mut disks = Vec::new();
if let Ok(o) = out {
let text = String::from_utf8_lossy(&o.stdout);
for line in text.lines() {
let cols: Vec<&str> = line.splitn(3, char::is_whitespace)
.filter(|s| !s.is_empty())
.collect();
if cols.len() < 2 { continue; }
if o.status.success() {
let text = String::from_utf8_lossy(&o.stdout);
for line in text.lines() {
// split_whitespace ardışık tüm boşlukları tek bir ayırıcı gibi yakalar
let mut tokens = line.split_whitespace();
let name_raw = match tokens.next() {
Some(n) => n,
None => continue,
};
let name_raw = cols[0]; // örn. "sda" veya "nvme0n1"
// loop, ram, sr aygıtlarını atla
if name_raw.starts_with("loop")
|| name_raw.starts_with("ram")
|| name_raw.starts_with("sr")
{ continue; }
// loop, ram, sr (CD-ROM) ve zram aygıtlarını atla
if name_raw.starts_with("loop")
|| name_raw.starts_with("ram")
|| name_raw.starts_with("sr")
|| name_raw.starts_with("zram")
{ continue; }
let size_bytes: u64 = cols[1].trim().parse().unwrap_or(0);
let model = if cols.len() >= 3 { cols[2].trim() } else { "" };
let size_str = match tokens.next() {
Some(s) => s,
None => continue,
};
disks.push(crate::installer::DiskInfo {
name: format!("/dev/{}", name_raw),
model: model.to_string(),
size_gb: size_bytes / 1_073_741_824,
size_bytes,
});
let size_bytes: u64 = size_str.parse().unwrap_or(0);
// Eğer boyut 0 ise ekleme yapma ki lsblk patlarsa fallback çalışabilsin
if size_bytes == 0 { continue; }
// Kalan tüm token'lar model ismidir (örn: ["VBOX", "HARDDISK"])
let model_tokens: Vec<&str> = tokens.collect();
let model = model_tokens.join(" ");
disks.push(crate::installer::DiskInfo {
name: format!("/dev/{}", name_raw),
model,
size_gb: size_bytes / 1_073_741_824,
size_bytes,
});
}
}
}
// lsblk başarısız olduysa /sys/block fallback
// lsblk başarısız olduysa veya geçerli disk bulamadıysa temiz /sys/block fallback'i çalışır
if disks.is_empty() {
if let Ok(rd) = std::fs::read_dir("/sys/block") {
for entry in rd.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.starts_with("loop") || name.starts_with("ram") { continue; }
if name.starts_with("loop")
|| name.starts_with("ram")
|| name.starts_with("sr")
|| name.starts_with("zram")
{ continue; }
let size_path = format!("/sys/block/{}/size", name);
let size_sectors: u64 = std::fs::read_to_string(&size_path)
.unwrap_or_default().trim().parse().unwrap_or(0);
let size_bytes = size_sectors * 512;
if size_bytes == 0 { continue; }
let model_path = format!("/sys/block/{}/device/model", name);
let model = std::fs::read_to_string(&model_path)
@@ -74,7 +168,7 @@ fn list_block_devices() -> Vec<crate::installer::DiskInfo> {
use crate::installer::{
CustomPartition, DiskInfo, FsType, GlobalState, InstallerStep,
PartitionPlan, PartitionTableType,
PartitionPlan, PartitionTableType, VolumeGroup,
};
const MIN_DISK_GB: u64 = 10;
@@ -96,18 +190,35 @@ struct ManualState {
add_size_str: String,
/// Diyalog: bağlama noktası
add_mountpoint: String,
/// Diyalog: LUKS şifrele
add_encrypt: bool,
/// Diyalog: LUKS parola
add_luks_password: String,
/// Silinmek istenen bölüm indeksi (onay isteği)
delete_idx: Option<usize>,
/// Genel hata mesajı
error: Option<String>,
/// Yeni VG adı (LVM)
add_vg_name: String,
// ── LVM Logical Volume Ekleme State'leri ───────────────────
adding_lv: bool,
add_lv_name: String,
add_lv_vg_idx: usize,
add_lv_size_str: String,
add_lv_fstype: usize,
add_lv_mountpoint: String,
}
const FS_OPTIONS: &[(&str, FsType)] = &[
("ext4", FsType::Ext4),
("ext3", FsType::Ext3),
("btrfs", FsType::Btrfs),
("xfs", FsType::Xfs),
("FAT32 (EFI)", FsType::Fat32),
("NTFS", FsType::Ntfs),
("swap", FsType::Swap),
("LVM PV", FsType::Lvm),
];
// ─────────────────────────────────────────────
@@ -118,6 +229,7 @@ pub struct PartitionStep {
scanned: bool,
is_uefi: bool,
manual: ManualState,
show_luks_password: bool,
}
impl Default for PartitionStep {
@@ -126,6 +238,7 @@ impl Default for PartitionStep {
scanned: false,
is_uefi: std::path::Path::new("/sys/firmware/efi").exists(),
manual: ManualState::default(),
show_luks_password: false,
}
}
}
@@ -163,12 +276,19 @@ fn make_plan(disk: &DiskInfo, is_uefi: bool) -> Option<PartitionPlan> {
efi_mb,
swap_mb,
root_mb,
encrypt_root: false,
luks_password: String::new(),
})
}
/// Manuel listedeki bölümlerin geçerliliğini kontrol eder.
fn validate_manual(parts: &[CustomPartition], is_uefi: bool) -> Option<String> {
let has_root = parts.iter().any(|p| p.mountpoint == "/");
fn validate_manual(
parts: &[CustomPartition],
lvs: &[crate::installer::LogicalVolume],
is_uefi: bool,
) -> Option<String> {
let has_root = parts.iter().any(|p| p.mountpoint == "/")
|| lvs.iter().any(|lv| lv.mountpoint == "/");
let has_efi = parts.iter().any(|p| p.mountpoint == "/boot/efi" || p.fstype == FsType::Fat32);
if !has_root {
@@ -224,7 +344,7 @@ impl InstallerStep for PartitionStep {
state.partition_plan.is_some()
} else {
!state.custom_partitions.is_empty()
&& validate_manual(&state.custom_partitions, self.is_uefi).is_none()
&& validate_manual(&state.custom_partitions, &state.logical_volumes, self.is_uefi).is_none()
}
}
}
@@ -234,7 +354,7 @@ impl InstallerStep for PartitionStep {
// ─────────────────────────────────────────────
impl PartitionStep {
fn show_auto_mode(&self, ui: &mut egui::Ui, state: &mut GlobalState) {
fn show_auto_mode(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.group(|ui| {
ui.label(t!("select_disk_label"));
ui.add_space(4.0);
@@ -296,6 +416,30 @@ impl PartitionStep {
ui.add_space(8.0);
ui.label(format!("{}: {}", t!("partition_table_type"), plan.table_type));
// ── LUKS şifreleme ─────────────────────────────────────
ui.add_space(12.0);
ui.separator();
ui.add_space(8.0);
let plan = state.partition_plan.as_mut().unwrap();
ui.checkbox(&mut plan.encrypt_root, t!("luks_enable_root"));
if plan.encrypt_root {
ui.horizontal(|ui| {
ui.label(t!("luks_password"));
if self.show_luks_password {
ui.text_edit_singleline(&mut plan.luks_password);
} else {
ui.add(egui::TextEdit::singleline(&mut plan.luks_password).password(true));
}
let eye = if self.show_luks_password { "🙈" } else { "👁" };
if ui.small_button(eye).clicked() {
self.show_luks_password = !self.show_luks_password;
}
});
if plan.luks_password.len() < 4 && !plan.luks_password.is_empty() {
ui.colored_label(egui::Color32::YELLOW, t!("luks_password_weak"));
}
}
ui.add_space(10.0);
egui::Frame::none()
.fill(egui::Color32::from_rgba_unmultiplied(200, 60, 40, 25))
@@ -329,8 +473,9 @@ impl PartitionStep {
let label = egui::RichText::new(format!("{}{} GB ({})", disk.name, disk.size_gb, disk.model)).strong().color(crate::ui::theme::c_accent());
if ui.radio(selected, label).clicked() {
state.selected_disk = Some(disk.name.clone());
// Disk değişince listeyi sıfırla
state.custom_partitions.clear();
// Disk değişince mevcut bölümleri oku ve listeyi güncelle.
// Böylece kullanıcı diskin mevcut düzenini görebilir.
state.custom_partitions = read_existing_partitions(&disk.name);
self.manual.error = None;
}
}
@@ -375,8 +520,12 @@ impl PartitionStep {
};
ui.label(&part.device);
let mut fstype_label = part.fstype.label().to_string();
if part.encrypt {
fstype_label = format!("🔐 {}", fstype_label);
}
ui.label(&size_str);
ui.label(part.fstype.label());
ui.label(fstype_label);
ui.label(&part.mountpoint);
if ui.small_button("🗑").on_hover_text(t!("mp_delete")).clicked() {
delete_idx = Some(i);
@@ -451,7 +600,7 @@ impl PartitionStep {
// Dosya sistemi
ui.label(t!("mp_fstype"));
egui::ComboBox::from_id_source("fs_combo")
.selected_text(FS_OPTIONS[self.manual.add_fstype].0)
.selected_text(FS_OPTIONS.get(self.manual.add_fstype).map(|x| x.0).unwrap_or("?"))
.show_ui(ui, |ui| {
for (i, (label, _)) in FS_OPTIONS.iter().enumerate() {
ui.selectable_value(
@@ -472,6 +621,33 @@ impl PartitionStep {
.hint_text(t!("mp_add_dialog_hint"));
ui.add(mp_field);
ui.end_row();
// LUKS şifreleme
ui.label(t!("luks_enable_root"));
let fstype = FS_OPTIONS.get(self.manual.add_fstype).map(|x| x.1.clone()).unwrap_or(FsType::Ext4);
let can_encrypt = matches!(fstype, FsType::Ext4 | FsType::Ext3 | FsType::Btrfs | FsType::Xfs);
ui.add_enabled_ui(can_encrypt, |ui| {
ui.checkbox(&mut self.manual.add_encrypt, "");
});
if !can_encrypt { self.manual.add_encrypt = false; }
ui.end_row();
if self.manual.add_encrypt {
ui.label(t!("luks_password"));
let pw = &mut self.manual.add_luks_password;
ui.horizontal(|ui| {
if self.show_luks_password {
ui.text_edit_singleline(pw);
} else {
ui.add(egui::TextEdit::singleline(pw).password(true));
}
let eye = if self.show_luks_password { "🙈" } else { "👁" };
if ui.small_button(eye).clicked() {
self.show_luks_password = !self.show_luks_password;
}
});
ui.end_row();
}
});
ui.add_space(8.0);
@@ -481,19 +657,26 @@ impl PartitionStep {
let size_mb: u64 = self.manual.add_size_str
.trim().parse().unwrap_or(0);
let mp = self.manual.add_mountpoint.trim().to_string();
let fstype = FS_OPTIONS[self.manual.add_fstype].1.clone();
let fstype = FS_OPTIONS.get(self.manual.add_fstype).map(|x| x.1.clone()).unwrap_or(FsType::Ext4);
let part_num = state.custom_partitions.len() + 1;
let device = crate::installer::part_path(&disk, part_num as u32);
if mp.is_empty() {
self.manual.error = Some(t!("mp_error_empty_mountpoint").to_string());
} else {
let encrypt = self.manual.add_encrypt;
let luks_password = if encrypt { self.manual.add_luks_password.clone() } else { String::new() };
state.custom_partitions.push(CustomPartition {
device,
size_mb,
fstype,
mountpoint: mp,
encrypt,
luks_password,
luks_name: String::new(),
});
self.manual.add_encrypt = false;
self.manual.add_luks_password.clear();
self.manual.error = None;
self.manual.adding = false;
}
@@ -505,13 +688,234 @@ impl PartitionStep {
});
}
// ── LVM Yapılandırması ──────────────────────────────────
ui.add_space(12.0);
let has_lvm_pvs = state.custom_partitions.iter().any(|p| p.fstype == FsType::Lvm);
if has_lvm_pvs {
ui.label(egui::RichText::new("LVM Volume Groups").strong().size(14.0));
ui.add_space(4.0);
egui::Grid::new("lvm_vg_grid")
.num_columns(4)
.spacing([12.0, 6.0])
.striped(true)
.show(ui, |ui| {
ui.label("VG Name");
ui.label("PV Devices");
ui.label("Size");
ui.label("");
ui.end_row();
let mut remove_vg: Option<usize> = None;
for (vi, vg) in state.volume_groups.iter().enumerate() {
let pv_list = vg.pv_devices.join(", ");
let size_str = if vg.size_mb >= 1024 {
format!("{:.1} GB", vg.size_mb as f64 / 1024.0)
} else {
format!("{} MB", vg.size_mb)
};
ui.label(&vg.name);
ui.label(&pv_list);
ui.label(&size_str);
if ui.small_button("🗑").clicked() {
remove_vg = Some(vi);
}
ui.end_row();
}
if let Some(vi) = remove_vg {
state.volume_groups.remove(vi);
}
// Yeni VG ekleme
ui.horizontal(|ui| {
ui.text_edit_singleline(&mut self.manual.add_vg_name);
if ui.small_button(" VG").clicked()
&& !self.manual.add_vg_name.is_empty()
{
let pvs: Vec<String> = state.custom_partitions.iter()
.filter(|p| p.fstype == FsType::Lvm)
.map(|p| p.device.clone())
.collect();
let size: u64 = state.custom_partitions.iter()
.filter(|p| p.fstype == FsType::Lvm)
.map(|p| p.size_mb)
.sum();
state.volume_groups.push(VolumeGroup {
name: self.manual.add_vg_name.clone(),
pv_devices: pvs,
size_mb: size,
});
self.manual.add_vg_name.clear();
}
});
ui.label("");
ui.label("");
ui.end_row();
});
// ── LVM Logical Volumes Tablosu ──────────────────────────
let has_vgs = !state.volume_groups.is_empty();
if has_vgs {
ui.add_space(12.0);
ui.label(egui::RichText::new(t!("lvm_lv_title")).strong().size(14.0));
ui.add_space(4.0);
let mut remove_lv: Option<usize> = None;
egui::Grid::new("lvm_lv_grid")
.num_columns(5)
.spacing([12.0, 6.0])
.striped(true)
.show(ui, |ui| {
ui.label(egui::RichText::new(t!("lvm_lv_name")).strong().color(crate::ui::theme::c_text()));
ui.label(egui::RichText::new(t!("lvm_lv_vg")).strong().color(crate::ui::theme::c_text()));
ui.label(egui::RichText::new(t!("mp_size")).strong().color(crate::ui::theme::c_text()));
ui.label(egui::RichText::new(t!("mp_mountpoint")).strong().color(crate::ui::theme::c_text()));
ui.label(egui::RichText::new(t!("mp_action")).strong().color(crate::ui::theme::c_text()));
ui.end_row();
for (li, lv) in state.logical_volumes.iter().enumerate() {
let size_str = if lv.size_mb == 0 {
t!("mp_size_remaining").to_string()
} else {
format!("{} MB", lv.size_mb)
};
ui.label(&lv.name);
ui.label(&lv.vg_name);
ui.label(size_str);
ui.label(&lv.mountpoint);
if ui.small_button("🗑").clicked() {
remove_lv = Some(li);
}
ui.end_row();
}
});
if let Some(li) = remove_lv {
state.logical_volumes.remove(li);
}
ui.add_space(8.0);
// "Logical Volume Ekle" butonu
let lv_btn = egui::Button::new(format!(" {}", t!("lvm_lv_add")))
.fill(accent.gamma_multiply(0.15));
if ui.add(lv_btn).clicked() {
self.manual.adding_lv = true;
self.manual.add_lv_name.clear();
self.manual.add_lv_vg_idx = 0;
self.manual.add_lv_size_str.clear();
self.manual.add_lv_fstype = 0;
self.manual.add_lv_mountpoint.clear();
}
}
}
// ── Logical Volume Ekleme Diyaloğu ────────────────────────
if self.manual.adding_lv {
egui::Window::new(t!("lvm_lv_add"))
.collapsible(false)
.resizable(false)
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
.show(ui.ctx(), |ui| {
egui::Grid::new("add_lv_grid")
.num_columns(2)
.spacing([12.0, 8.0])
.show(ui, |ui| {
// Hacim Grubu (VG) seçimi
ui.label(t!("lvm_lv_vg"));
let current_vg = state.volume_groups.get(self.manual.add_lv_vg_idx)
.map(|vg| vg.name.as_str())
.unwrap_or("?");
egui::ComboBox::from_id_source("lv_vg_combo")
.selected_text(current_vg)
.show_ui(ui, |ui| {
for (vi, vg) in state.volume_groups.iter().enumerate() {
ui.selectable_value(
&mut self.manual.add_lv_vg_idx, vi, &vg.name
);
}
});
ui.end_row();
// LV Adı
ui.label(t!("lvm_lv_name"));
ui.text_edit_singleline(&mut self.manual.add_lv_name);
ui.end_row();
// Dosya sistemi
ui.label(t!("mp_fstype"));
egui::ComboBox::from_id_source("lv_fs_combo")
.selected_text(FS_OPTIONS.get(self.manual.add_lv_fstype).map(|x| x.0).unwrap_or("?"))
.show_ui(ui, |ui| {
for (i, (label, _)) in FS_OPTIONS.iter().enumerate() {
ui.selectable_value(
&mut self.manual.add_lv_fstype, i, *label
);
}
});
ui.end_row();
// Boyut
ui.label(t!("mp_size_hint"));
ui.text_edit_singleline(&mut self.manual.add_lv_size_str);
ui.end_row();
// Bağlama noktası
ui.label(t!("mp_mountpoint"));
let mp_field = egui::TextEdit::singleline(&mut self.manual.add_lv_mountpoint)
.hint_text(t!("mp_add_dialog_hint"));
ui.add(mp_field);
ui.end_row();
});
ui.add_space(8.0);
ui.horizontal(|ui| {
let ok_btn = egui::Button::new(t!("mp_add_btn")).fill(accent.gamma_multiply(0.2));
if ui.add(ok_btn).clicked() {
let size_mb: u64 = self.manual.add_lv_size_str.trim().parse().unwrap_or(0);
let mp = self.manual.add_lv_mountpoint.trim().to_string();
let lv_name = self.manual.add_lv_name.trim().to_string();
let fstype = FS_OPTIONS.get(self.manual.add_lv_fstype).map(|x| x.1.clone()).unwrap_or(FsType::Ext4);
if lv_name.is_empty() {
self.manual.error = Some("LV Adı boş olamaz.".to_string());
} else if mp.is_empty() {
self.manual.error = Some(t!("mp_error_empty_mountpoint").to_string());
} else if let Some(vg) = state.volume_groups.get(self.manual.add_lv_vg_idx) {
// VG sınır doğrulaması
let current_sum: u64 = state.logical_volumes.iter()
.filter(|lv| lv.vg_name == vg.name)
.map(|lv| lv.size_mb)
.sum();
if current_sum + size_mb > vg.size_mb && size_mb > 0 {
self.manual.error = Some(t!("lvm_lv_error_size").to_string());
} else {
state.logical_volumes.push(crate::installer::LogicalVolume {
name: lv_name,
vg_name: vg.name.clone(),
size_mb,
fstype,
mountpoint: mp,
});
self.manual.error = None;
self.manual.adding_lv = false;
}
} else {
self.manual.error = Some(t!("lvm_lv_error_vg").to_string());
}
}
if ui.button(t!("cancel")).clicked() {
self.manual.adding_lv = false;
}
});
});
}
// ── Doğrulama mesajları ─────────────────────────────────
ui.add_space(6.0);
if let Some(ref err) = self.manual.error {
ui.colored_label(egui::Color32::from_rgb(217, 0, 91), err);
}
if !state.custom_partitions.is_empty() {
if let Some(warn) = validate_manual(&state.custom_partitions, self.is_uefi) {
if let Some(warn) = validate_manual(&state.custom_partitions, &state.logical_volumes, self.is_uefi) {
ui.colored_label(egui::Color32::from_rgb(220, 160, 40), warn);
} else {
ui.colored_label(
+516
View File
@@ -0,0 +1,516 @@
//! Kurtarma modu adımı.
//!
//! Özellikler:
//! - Mevcut Linux kurulumlarını tarar (lsblk)
//! - Seçilen bölümü /mnt'a bağlar
//! - chroot kabuğu başlatır
//! - GRUB'u yeniden yükler
//! - **Pisi paket geçmişini listeler ve `pisi takeback <N>` ile geri alır**
use eframe::egui;
use rust_i18n::t;
use crate::installer::{GlobalState, InstallerStep};
use crate::ui::theme;
/// Pisi geçmişindeki tek bir işlem kaydı.
#[derive(Clone, Debug)]
struct PisiHistoryEntry {
/// İşlem numarası (takeback argümanı)
pub op: u32,
/// Tarih/saat dizisi
pub date: String,
/// İşlem türü (install, remove, upgrade, …)
pub op_type: String,
/// Etkilenen paket sayısı veya açıklaması
pub summary: String,
}
/// Pisi geçmiş geri alma modunun durumu.
#[derive(Default)]
struct PisiHistoryState {
entries: Vec<PisiHistoryEntry>,
selected: Option<usize>,
scanned: bool,
running: bool,
result_msg: Option<String>,
result_ok: Option<bool>,
}
#[derive(Default)]
pub struct RescueStep {
detected: Vec<String>,
selected: Option<usize>,
log: Vec<String>,
mount_done: bool,
pisi: PisiHistoryState,
/// Aktif sekme: 0=Chroot/GRUB, 1=Pisi Geçmiş
tab: usize,
}
impl RescueStep {
fn detect_installations(&mut self) {
self.detected.clear();
self.mount_done = false;
if let Ok(out) = std::process::Command::new("lsblk")
.args(["-o", "NAME,FSTYPE,MOUNTPOINT", "-l", "-n"])
.output()
{
let text = String::from_utf8_lossy(&out.stdout);
for line in text.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
let fstype = parts[1];
if fstype == "ext4" || fstype == "btrfs" || fstype == "xfs" {
let dev = format!("/dev/{}", parts[0]);
let mp = parts.get(2).unwrap_or(&"-").to_string();
self.detected.push(format!("{} ({}) [{}]", dev, fstype, mp));
}
}
}
}
}
/// Seçili bölümü /mnt'a bağlar. Başarılıysa `true` döner.
fn mount_selected(&mut self) -> bool {
if let Some(idx) = self.selected {
if let Some(entry) = self.detected.get(idx) {
// Entry: "/dev/sda1 (ext4) [-]" — sadece ilk token aygıt yolu
let dev = entry.split_whitespace().next().unwrap_or("");
if dev.is_empty() { return false; }
// /mnt boşsa bağla
let r = std::process::Command::new("mount")
.args([dev, "/mnt"])
.status();
if let Ok(s) = r {
if s.success() {
self.mount_done = true;
self.log.push(format!("{} → /mnt bağlandı.", dev));
return true;
} else {
self.log.push(format!("{} bağlanamadı (zaten bağlı veya hata).", dev));
self.mount_done = true; // Belki zaten bağlı
return true;
}
}
}
}
false
}
fn run_rescue_cmd(cmd: &str, args: &[&str]) -> Result<String, String> {
let out = std::process::Command::new(cmd)
.args(args)
.output()
.map_err(|e| format!("{} hatası: {}", cmd, e))?;
if out.status.success() {
Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
} else {
Err(String::from_utf8_lossy(&out.stderr).trim().to_string())
}
}
/// `pisi history` çıktısını ayrıştırır.
/// Örnek satır: "Operation 42 [2024-03-15 12:00:01]: install [vim]"
fn parse_pisi_history(raw: &str) -> Vec<PisiHistoryEntry> {
let mut entries = Vec::new();
for line in raw.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') { continue; }
// "Operation 42 [2024-03-15 12:00:01]: install [vim, curl]"
if let Some(rest) = line.strip_prefix("Operation ") {
let mut parts = rest.splitn(2, ' ');
let op_str = parts.next().unwrap_or("0");
let remainder = parts.next().unwrap_or("");
let op: u32 = op_str.parse().unwrap_or(0);
// Tarih: "[2024-03-15 12:00:01]"
let date = if let Some(start) = remainder.find('[') {
if let Some(end) = remainder.find(']') {
remainder[start+1..end].to_string()
} else { String::new() }
} else { String::new() };
// Tür + özet: ": install [vim, curl]"
let (op_type, summary) = if let Some(colon) = remainder.rfind("]: ") {
let after = &remainder[colon+3..];
if let Some(sp) = after.find(' ') {
(after[..sp].to_string(), after[sp+1..].to_string())
} else {
(after.to_string(), String::new())
}
} else { ("unknown".to_string(), remainder.to_string()) };
entries.push(PisiHistoryEntry { op, date, op_type, summary });
}
}
// En yeni önce
entries.sort_by(|a, b| b.op.cmp(&a.op));
entries
}
/// /mnt chroot içinde `pisi history` çalıştırır ve ayrıştırır.
fn scan_pisi_history(&mut self) {
self.pisi.entries.clear();
self.pisi.result_msg = None;
self.pisi.selected = None;
let out = std::process::Command::new("chroot")
.args(["/mnt", "pisi", "history"])
.output();
match out {
Ok(o) => {
let text = String::from_utf8_lossy(&o.stdout).to_string();
let entries = Self::parse_pisi_history(&text);
if entries.is_empty() {
self.pisi.result_msg = Some(t!("rescue_pisi_history_empty").to_string());
self.pisi.result_ok = Some(false);
}
self.pisi.entries = entries;
}
Err(e) => {
self.pisi.result_msg = Some(format!("{}", e));
self.pisi.result_ok = Some(false);
}
}
self.pisi.scanned = true;
}
/// `pisi takeback <op>` çalıştırır.
fn run_takeback(&mut self, op: u32) {
self.pisi.running = true;
self.pisi.result_msg = Some(t!("rescue_pisi_takeback_start").to_string());
self.pisi.result_ok = None;
let op_str = op.to_string();
let out = std::process::Command::new("chroot")
.args(["/mnt", "pisi", "takeback", &op_str])
.output();
self.pisi.running = false;
match out {
Ok(o) if o.status.success() => {
self.pisi.result_ok = Some(true);
self.pisi.result_msg = Some(t!("rescue_pisi_history_ok").to_string());
self.log.push(format!("✓ pisi takeback {} başarılı.", op));
}
Ok(o) => {
let err = String::from_utf8_lossy(&o.stderr).trim().to_string();
self.pisi.result_ok = Some(false);
self.pisi.result_msg = Some(t!("rescue_pisi_history_err", err = err).to_string());
self.log.push(format!("❌ pisi takeback {}: {}", op, err));
}
Err(e) => {
self.pisi.result_ok = Some(false);
self.pisi.result_msg = Some(t!("rescue_pisi_history_err", err = e.to_string()).to_string());
}
}
}
// ─── UI Panelleri ────────────────────────────────────────────────
fn show_chroot_panel(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.add_space(6.0);
// Bölüm listesi
ui.label(egui::RichText::new(t!("rescue_detected")).strong().color(theme::c_text()));
ui.add_space(4.0);
egui::ScrollArea::vertical()
.id_source("rescue_partition_list")
.max_height(130.0)
.show(ui, |ui| {
for (i, entry) in self.detected.iter().enumerate() {
let is_selected = self.selected == Some(i);
let label = egui::RichText::new(entry)
.family(egui::FontFamily::Monospace)
.color(if is_selected { theme::c_accent() } else { theme::c_text() });
if ui.selectable_label(is_selected, label).clicked() {
self.selected = Some(i);
self.mount_done = false;
}
}
});
ui.add_space(10.0);
// İşlem butonları
ui.horizontal(|ui| {
// 1. Bağla
if ui.add_enabled(
self.selected.is_some() && !self.mount_done,
theme::secondary_button("📂 /mnt'a Bağla"),
).clicked() {
self.mount_selected();
}
// 2. Chroot
if ui.add_enabled(
self.mount_done,
theme::secondary_button(&t!("rescue_chroot")),
).clicked() {
self.log.push(t!("rescue_chroot_start").to_string());
match Self::run_rescue_cmd("chroot", &["/mnt", "/bin/bash", "-c", "echo 'chroot OK'"]) {
Ok(msg) => self.log.push(format!("{}", msg)),
Err(e) => self.log.push(format!("{}", e)),
}
}
// 3. GRUB yeniden yükle
if ui.add_enabled(
self.mount_done,
theme::secondary_button(&t!("rescue_grub")),
).clicked() {
self.log.push(t!("rescue_grub_start").to_string());
let boot_dev = state.bootloader_device.clone();
let steps: &[(&str, &[&str])] = &[
("mount", &["--bind", "/proc", "/mnt/proc"]),
("mount", &["--bind", "/sys", "/mnt/sys"]),
("mount", &["--bind", "/dev", "/mnt/dev"]),
];
for (cmd, args) in steps {
match Self::run_rescue_cmd(cmd, args) {
Ok(m) if !m.is_empty() => self.log.push(m),
Ok(_) => {},
Err(e) => self.log.push(format!("{}", e)),
}
}
// grub2-install
let grub_args: Vec<&str> = vec!["grub2-install", &boot_dev];
match Self::run_rescue_cmd("chroot", &{
let mut a: Vec<&str> = vec!["/mnt"];
a.extend_from_slice(&grub_args);
a
}) {
Ok(m) => self.log.push(format!("✓ grub2-install: {}", m)),
Err(e) => self.log.push(format!("❌ grub2-install: {}", e)),
}
// grub2-mkconfig
match Self::run_rescue_cmd("chroot", &["/mnt", "grub2-mkconfig", "-o", "/boot/grub2/grub.cfg"]) {
Ok(m) => self.log.push(format!("✓ grub2-mkconfig: {}", m)),
Err(e) => self.log.push(format!("❌ grub2-mkconfig: {}", e)),
}
}
});
}
fn show_pisi_panel(&mut self, ui: &mut egui::Ui) {
ui.add_space(6.0);
ui.label(
egui::RichText::new(t!("rescue_pisi_history_desc"))
.size(12.0)
.color(theme::c_text_dim()),
);
ui.add_space(8.0);
if !self.mount_done {
theme::warning_box(ui, "Önce 'Chroot & GRUB' sekmesinden bölümü /mnt'a bağlayın.");
return;
}
// Tara butonu
ui.horizontal(|ui| {
if ui.add(theme::secondary_button(&t!("rescue_pisi_history_scan"))).clicked() {
self.scan_pisi_history();
}
if self.pisi.scanned {
ui.label(
egui::RichText::new(format!("{} işlem listelendi", self.pisi.entries.len()))
.size(12.0)
.color(theme::c_text_dim()),
);
}
});
if self.pisi.entries.is_empty() {
if let Some(ref msg) = self.pisi.result_msg.clone() {
ui.add_space(6.0);
theme::warning_box(ui, msg);
}
return;
}
ui.add_space(8.0);
// Geçmiş tablosu
egui::ScrollArea::vertical()
.id_source("pisi_history_scroll")
.max_height(200.0)
.show(ui, |ui| {
egui::Grid::new("pisi_history_grid")
.num_columns(4)
.spacing([12.0, 4.0])
.striped(true)
.show(ui, |ui| {
// Başlık satırı
ui.label(egui::RichText::new("#").strong().color(theme::c_text_dim()));
ui.label(egui::RichText::new("Tarih").strong().color(theme::c_text_dim()));
ui.label(egui::RichText::new("İşlem").strong().color(theme::c_text_dim()));
ui.label(egui::RichText::new("Paketler").strong().color(theme::c_text_dim()));
ui.end_row();
for (i, entry) in self.pisi.entries.iter().enumerate() {
let is_selected = self.pisi.selected == Some(i);
let row_color = if is_selected {
theme::c_accent()
} else {
theme::c_text()
};
let op_label = egui::RichText::new(entry.op.to_string())
.family(egui::FontFamily::Monospace)
.color(row_color);
if ui.selectable_label(is_selected, op_label).clicked() {
self.pisi.selected = Some(i);
}
ui.label(egui::RichText::new(&entry.date).size(11.0).color(row_color));
// İşlem türüne renkli badge
let type_color = match entry.op_type.as_str() {
"install" => egui::Color32::from_rgb(80, 180, 80),
"remove" => egui::Color32::from_rgb(200, 70, 70),
"upgrade" => egui::Color32::from_rgb(70, 140, 220),
_ => theme::c_text_dim(),
};
ui.colored_label(type_color, &entry.op_type);
// Özet — truncate
let summary = if entry.summary.len() > 50 {
format!("{}", &entry.summary[..50])
} else {
entry.summary.clone()
};
ui.label(egui::RichText::new(summary).size(11.0).color(theme::c_text_dim()));
ui.end_row();
}
});
});
ui.add_space(10.0);
// Geri al butonu
let can_takeback = self.pisi.selected.is_some() && !self.pisi.running;
if ui.add_enabled(can_takeback, theme::primary_button(&t!("rescue_pisi_takeback"))).clicked() {
if let Some(idx) = self.pisi.selected {
let op = self.pisi.entries[idx].op;
self.run_takeback(op);
}
}
// Sonuç mesajı
if let Some(ref msg) = self.pisi.result_msg.clone() {
ui.add_space(6.0);
match self.pisi.result_ok {
Some(true) => theme::success_box(ui, msg),
Some(false) => theme::error_box(ui, msg),
None => { ui.label(msg); }
}
}
}
}
impl InstallerStep for RescueStep {
fn name(&self) -> String { t!("rescue").to_string() }
fn on_enter(&mut self, _state: &mut GlobalState) {
self.detect_installations();
}
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
theme::section_heading(ui, &t!("rescue_title"));
ui.label(
egui::RichText::new(t!("rescue_description"))
.color(theme::c_text_dim())
.size(13.0),
);
ui.add_space(10.0);
if self.detected.is_empty() {
theme::warning_box(ui, &t!("rescue_no_installations"));
ui.add_space(6.0);
if ui.add(theme::secondary_button(&t!("rescue_scan"))).clicked() {
self.detect_installations();
}
return;
}
// ─── Sekmeler ───────────────────────────────────────────────
ui.horizontal(|ui| {
let tab_labels = [
("💻 Chroot & GRUB", 0usize),
("📜 Pisi Geçmiş", 1usize),
];
for (label, idx) in &tab_labels {
let active = self.tab == *idx;
let rt = egui::RichText::new(*label)
.strong()
.color(if active { theme::c_accent() } else { theme::c_text_dim() });
if ui.selectable_label(active, rt).clicked() {
self.tab = *idx;
}
ui.separator();
}
});
ui.separator();
ui.add_space(4.0);
match self.tab {
0 => self.show_chroot_panel(ui, state),
1 => self.show_pisi_panel(ui),
_ => {}
}
ui.add_space(12.0);
// Log alanı — her iki sekmede de görünür
if !self.log.is_empty() {
ui.separator();
ui.add_space(4.0);
ui.label(
egui::RichText::new("Log")
.size(11.0)
.color(theme::c_text_dim()),
);
egui::ScrollArea::vertical()
.id_source("rescue_log")
.max_height(160.0)
.stick_to_bottom(true)
.show(ui, |ui| {
egui::Frame::group(ui.style())
.inner_margin(egui::Margin::same(8.0))
.show(ui, |ui| {
for line in &self.log {
let color = if line.starts_with("") {
theme::c_success()
} else if line.starts_with("") {
theme::c_error()
} else if line.starts_with("") {
theme::c_warning()
} else {
theme::c_text_dim()
};
ui.colored_label(
color,
egui::RichText::new(line)
.size(11.0)
.family(egui::FontFamily::Monospace),
);
}
});
});
}
// Geri dön butonu
ui.add_space(8.0);
if ui.add(theme::secondary_button(&t!("rescue_return"))).clicked() {
state.rescue_mode = false;
}
}
fn is_complete(&self, _state: &GlobalState) -> bool {
false // Rescue modu her zaman "tamamlanmamış" — normal kuruluma dönmek için rescue_mode=false
}
}
+33
View File
@@ -78,6 +78,39 @@ impl InstallerStep for SummaryStep {
summary_row(ui, t!("summary_custom_partitions"), &plan_lines.join("\n"));
}
// Tarih/Saat
let dt_str = if state.use_ntp {
t!("summary_ntp_enabled").to_string()
} else {
format!("{:04}-{:02}-{:02} {:02}:{:02} (NTP off)",
state.manual_year, state.manual_month, state.manual_day,
state.manual_hour, state.manual_minute)
};
summary_row(ui, t!("summary_datetime"), &dt_str);
// Bootloader
let bl_str = format!("{} ({}s timeout{})",
state.bootloader_device,
state.bootloader_timeout,
if state.bootloader_password.is_empty() { "" } else { ", password protected" }
);
summary_row(ui, t!("summary_bootloader"), &bl_str);
// Display Manager
let dm_str = format!("{} / {} ({})",
state.display_manager,
state.desktop_environment,
if state.autologin { t!("summary_autologin") } else { std::borrow::Cow::Borrowed("manual login") }
);
summary_row(ui, t!("summary_display_manager"), &dm_str);
let root_str = if state.root_password.is_empty() {
t!("summary_root_disabled").to_string()
} else {
t!("summary_root_enabled").to_string()
};
summary_row(ui, t!("summary_root"), &root_str);
summary_row(ui, t!("summary_username"), &state.username);
summary_row(ui, t!("summary_hostname"), &state.hostname);
});
+171 -90
View File
@@ -2,11 +2,13 @@ use eframe::egui;
use rust_i18n::t;
use crate::installer::{GlobalState, InstallerStep};
/// Kullanıcı adı, şifre ve hostname ayarları adımı.
/// Calamares'in Users modülüne karşılık gelir.
/// Kullanıcı adı, şifre, hostname ve yönetici (root) şifresi ayarları adımı.
pub struct UsersStep {
show_password: bool,
show_password_confirm: bool,
show_root_password: bool,
show_root_password_confirm: bool,
use_same_for_admin: bool,
}
impl Default for UsersStep {
@@ -14,6 +16,9 @@ impl Default for UsersStep {
Self {
show_password: false,
show_password_confirm: false,
show_root_password: false,
show_root_password_confirm: false,
use_same_for_admin: true,
}
}
}
@@ -77,110 +82,186 @@ impl InstallerStep for UsersStep {
}
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
ui.heading(t!("users_title"));
ui.label(t!("users_description"));
use crate::ui::theme;
theme::section_heading(ui, &t!("users_title"));
ui.label(
egui::RichText::new(t!("users_description"))
.color(theme::c_text_dim())
.size(13.0),
);
ui.add_space(12.0);
egui::Grid::new("users_grid")
.num_columns(2)
.spacing([12.0, 10.0])
// ─── Kullanıcı Bilgileri Grubu ──────────────────────────────────
egui::Frame::group(ui.style())
.fill(theme::c_bg_widget())
.rounding(6.0)
.inner_margin(egui::Margin::same(12.0))
.show(ui, |ui| {
// Kullanıcı adı
ui.label(t!("username_label"));
ui.horizontal(|ui| {
ui.text_edit_singleline(&mut state.username);
if !state.username.is_empty() {
if is_valid_username(&state.username) {
ui.colored_label(egui::Color32::from_rgb(80, 170, 80), "");
} else {
ui.colored_label(egui::Color32::from_rgb(200, 60, 40), "");
}
}
});
ui.end_row();
ui.set_width(ui.available_width());
egui::Grid::new("users_grid")
.num_columns(2)
.spacing([12.0, 10.0])
.show(ui, |ui| {
// Kullanıcı adı
ui.label(egui::RichText::new(t!("username_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
ui.text_edit_singleline(&mut state.username);
if !state.username.is_empty() {
if is_valid_username(&state.username) {
ui.colored_label(theme::c_success(), "");
} else {
ui.colored_label(theme::c_error(), "");
}
}
});
ui.end_row();
// Şifre
ui.label(t!("password_label"));
ui.horizontal(|ui| {
if self.show_password {
ui.text_edit_singleline(&mut state.password);
} else {
ui.add(egui::TextEdit::singleline(&mut state.password).password(true));
}
if ui.small_button(if self.show_password { "🙈" } else { "👁" }).clicked() {
self.show_password = !self.show_password;
}
});
ui.end_row();
// Şifre
ui.label(egui::RichText::new(t!("password_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
if self.show_password {
ui.text_edit_singleline(&mut state.password);
} else {
ui.add(egui::TextEdit::singleline(&mut state.password).password(true));
}
if ui.small_button(if self.show_password { "🙈" } else { "👁" }).clicked() {
self.show_password = !self.show_password;
}
if !state.password.is_empty() {
let (label, color) = password_strength(&state.password);
ui.colored_label(color, label);
}
});
ui.end_row();
// Şifre gücü göstergesi
ui.label("");
if !state.password.is_empty() {
let (label, color) = password_strength(&state.password);
ui.colored_label(color, label);
}
ui.end_row();
// Şifre doğrulama
ui.label(egui::RichText::new(t!("password_confirm_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
if self.show_password_confirm {
ui.text_edit_singleline(&mut state.password_confirm);
} else {
ui.add(egui::TextEdit::singleline(&mut state.password_confirm).password(true));
}
if ui.small_button(if self.show_password_confirm { "🙈" } else { "👁" }).clicked() {
self.show_password_confirm = !self.show_password_confirm;
}
if !state.password_confirm.is_empty() {
if state.password == state.password_confirm {
ui.colored_label(theme::c_success(), "");
} else {
ui.colored_label(theme::c_error(), t!("passwords_no_match"));
}
}
});
ui.end_row();
// Şifre doğrulama
ui.label(t!("password_confirm_label"));
ui.horizontal(|ui| {
if self.show_password_confirm {
ui.text_edit_singleline(&mut state.password_confirm);
} else {
ui.add(egui::TextEdit::singleline(&mut state.password_confirm).password(true));
}
if ui.small_button(if self.show_password_confirm { "🙈" } else { "👁" }).clicked() {
self.show_password_confirm = !self.show_password_confirm;
}
if !state.password_confirm.is_empty() {
if state.password == state.password_confirm {
ui.colored_label(egui::Color32::from_rgb(80, 170, 80), "");
} else {
ui.colored_label(egui::Color32::from_rgb(200, 60, 40), t!("passwords_no_match"));
}
}
});
ui.end_row();
// Hostname
ui.label(t!("hostname_label"));
ui.horizontal(|ui| {
// Username değişince hostname otomatik güncellenir (boşsa)
if state.hostname.is_empty() && !state.username.is_empty() {
state.hostname = format!("{}-pc", state.username);
}
ui.text_edit_singleline(&mut state.hostname);
if !state.hostname.is_empty() {
if is_valid_hostname(&state.hostname) {
ui.colored_label(egui::Color32::from_rgb(80, 170, 80), "");
} else {
ui.colored_label(egui::Color32::from_rgb(200, 60, 40), "");
}
}
});
ui.end_row();
// Hostname
ui.label(egui::RichText::new(t!("hostname_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
if state.hostname.is_empty() && !state.username.is_empty() {
state.hostname = format!("{}-pc", state.username);
}
ui.text_edit_singleline(&mut state.hostname);
if !state.hostname.is_empty() {
if is_valid_hostname(&state.hostname) {
ui.colored_label(theme::c_success(), "");
} else {
ui.colored_label(theme::c_error(), "");
}
}
});
ui.end_row();
});
});
// Hata mesajları
ui.add_space(14.0);
// ─── Yönetici (root) Ayarları ─────────────────────────────────────
ui.checkbox(&mut self.use_same_for_admin, egui::RichText::new(t!("use_same_for_admin")).strong().color(theme::c_text()));
if self.use_same_for_admin {
state.root_password = state.password.clone();
state.root_password_confirm = state.password_confirm.clone();
} else {
ui.add_space(8.0);
egui::Frame::group(ui.style())
.fill(theme::c_bg_widget())
.rounding(6.0)
.inner_margin(egui::Margin::same(12.0))
.show(ui, |ui| {
ui.set_width(ui.available_width());
ui.label(egui::RichText::new(t!("admin_description")).size(12.0).color(theme::c_text_dim()));
ui.add_space(8.0);
egui::Grid::new("admin_grid")
.num_columns(2)
.spacing([12.0, 10.0])
.show(ui, |ui| {
// Root şifresi
ui.label(egui::RichText::new(t!("admin_password_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
if self.show_root_password {
ui.text_edit_singleline(&mut state.root_password);
} else {
ui.add(egui::TextEdit::singleline(&mut state.root_password).password(true));
}
if ui.small_button(if self.show_root_password { "🙈" } else { "👁" }).clicked() {
self.show_root_password = !self.show_root_password;
}
if !state.root_password.is_empty() && state.root_password.len() < 4 {
ui.colored_label(theme::c_error(), t!("admin_password_short"));
}
});
ui.end_row();
// Şifre doğrulama
ui.label(egui::RichText::new(t!("admin_password_confirm_label")).strong().color(theme::c_text()));
ui.horizontal(|ui| {
if self.show_root_password_confirm {
ui.text_edit_singleline(&mut state.root_password_confirm);
} else {
ui.add(egui::TextEdit::singleline(&mut state.root_password_confirm).password(true));
}
if ui.small_button(if self.show_root_password_confirm { "🙈" } else { "👁" }).clicked() {
self.show_root_password_confirm = !self.show_root_password_confirm;
}
if !state.root_password_confirm.is_empty() {
if state.root_password == state.root_password_confirm {
ui.colored_label(theme::c_success(), "");
} else {
ui.colored_label(theme::c_error(), t!("passwords_no_match"));
}
}
});
ui.end_row();
});
});
}
// Hata mesajları gösterimi
ui.add_space(8.0);
if !state.username.is_empty() && !is_valid_username(&state.username) {
ui.colored_label(
egui::Color32::from_rgb(200, 60, 40),
t!("username_invalid"),
);
ui.colored_label(theme::c_error(), t!("username_invalid"));
}
if !state.hostname.is_empty() && !is_valid_hostname(&state.hostname) {
ui.colored_label(
egui::Color32::from_rgb(200, 60, 40),
t!("hostname_invalid"),
);
ui.colored_label(theme::c_error(), t!("hostname_invalid"));
}
}
fn is_complete(&self, state: &GlobalState) -> bool {
is_valid_username(&state.username)
let user_ok = is_valid_username(&state.username)
&& is_valid_hostname(&state.hostname)
&& !state.password.is_empty()
&& state.password == state.password_confirm
&& state.password.len() >= 4
&& state.password == state.password_confirm;
if self.use_same_for_admin {
user_ok
} else {
user_ok && (
state.root_password.is_empty()
|| (state.root_password.len() >= 4 && state.root_password == state.root_password_confirm)
)
}
}
}
+48 -57
View File
@@ -5,6 +5,7 @@
//! Masaüstü ortamına göre farklı slayt setleri yüklenebilir.
use eframe::egui;
use std::collections::HashMap;
use std::time::{Duration, Instant};
use rust_i18n::t;
use std::borrow::Cow;
@@ -19,22 +20,34 @@ pub struct Slide {
/// Masaüstü ortamına göre slayt seti döner.
/// ISO meta verisi ileride `/etc/livecd-desktop` gibi bir dosyadan okunabilir.
pub fn slides_for_desktop(desktop: &str) -> Vec<Slide> {
let mut slides = default_slides();
let specific_slides = match desktop {
"budgie" => budgie_slides(),
"cinnamon" => cinnamon_slides(),
"gnome" => gnome_slides(),
"kde" | "plasma" => kde_slides(),
"lxde" => lxde_slides(),
"lxqt" => lxqt_slides(),
"lumina" => lumina_slides(),
"mate" => mate_slides(),
"xfce" => xfce_slides(),
_ => vec![],
};
slides.extend(specific_slides);
slides
pub fn slides_for_desktop(desktop: &str, branding_slides: &[crate::branding::SlideItem]) -> Vec<Slide> {
if !branding_slides.is_empty() {
branding_slides
.iter()
.filter(|item| item.desktop.is_empty() || item.desktop.to_lowercase() == desktop.to_lowercase())
.map(|item| Slide {
icon: Box::leak(item.icon.clone().into_boxed_str()),
title: Cow::Owned(item.title.clone()),
description: Cow::Owned(item.description.clone()),
})
.collect()
} else {
let mut slides = default_slides();
let specific_slides = match desktop {
"budgie" => budgie_slides(),
"cinnamon" => cinnamon_slides(),
"gnome" => gnome_slides(),
"kde" | "plasma" => kde_slides(),
"lxde" => lxde_slides(),
"lxqt" => lxqt_slides(),
"lumina" => lumina_slides(),
"mate" => mate_slides(),
"xfce" => xfce_slides(),
_ => vec![],
};
slides.extend(specific_slides);
slides
}
}
fn default_slides() -> Vec<Slide> {
@@ -175,7 +188,7 @@ fn lxde_slides() -> Vec<Slide> {
description: t!("lxde_description"),
},
Slide {
icon: "lxde_openbox_title",
icon: "lxde_openbox",
title: t!("lxde_openbox_title"),
description: t!("lxde_openbox_description"),
},
@@ -185,7 +198,7 @@ fn lxde_slides() -> Vec<Slide> {
description: t!("lxde_pcmanfm_description"),
},
Slide {
icon: "lxde_lxpanel_title",
icon: "lxde_lxpanel",
title: t!("lxde_lxpanel_title"),
description: t!("lxde_lxpanel_description"),
},
@@ -200,7 +213,7 @@ fn lxqt_slides() -> Vec<Slide> {
description: t!("lxqt_description"),
},
Slide {
icon: "lxqt_panel_title",
icon: "lxqt_panel",
title: t!("lxqt_panel_title"),
description: t!("lxqt_panel_description"),
},
@@ -220,17 +233,17 @@ fn lumina_slides() -> Vec<Slide> {
description: t!("lumina_description"),
},
Slide {
icon: "lumina_applications_title",
icon: "lumina_applications",
title: t!("lumina_applications_title"),
description: t!("lumina_applications_description"),
},
Slide {
icon: "lumina_theme_title",
icon: "lumina_theme",
title: t!("lumina_theme_title"),
description: t!("lumina_theme_description"),
},
Slide {
icon: "lumina_panel_title",
icon: "lumina_panel",
title: t!("lumina_panel_title"),
description: t!("lumina_panel_description"),
},
@@ -250,12 +263,12 @@ fn mate_slides() -> Vec<Slide> {
description: t!("mate_caja_description"),
},
Slide {
icon: "mate_settings_title",
icon: "mate_settings",
title: t!("mate_settings_title"),
description: t!("mate_settings_description"),
},
Slide {
icon: "mate_applications_title",
icon: "mate_applications",
title: t!("mate_applications_title"),
description: t!("mate_applications_description"),
},
@@ -270,7 +283,7 @@ fn xfce_slides() -> Vec<Slide> {
description: t!("xfce_description"),
},
Slide {
icon: "xfce_panel_title",
icon: "xfce_panel",
title: t!("xfce_panel_title"),
description: t!("xfce_panel_description"),
},
@@ -288,20 +301,23 @@ fn xfce_slides() -> Vec<Slide> {
}
// ─── Slayt gösterisi widget'ı ────────────────────────────────────
// ─── Slayt gösterisi widget'ı ───────────────────────────────────────────
pub struct Slideshow {
slides: Vec<Slide>,
current: usize,
last_advance: Instant,
/// branding.toml'dan gelen ikon-adı → `file://` URI eşlemesi
icons: HashMap<String, String>,
}
impl Slideshow {
pub fn new(desktop: &str) -> Self {
pub fn new(desktop: &str, icons: HashMap<String, String>, branding_slides: Vec<crate::branding::SlideItem>) -> Self {
Self {
slides: slides_for_desktop(desktop),
slides: slides_for_desktop(desktop, &branding_slides),
current: 0,
last_advance: Instant::now(),
icons,
}
}
@@ -321,34 +337,9 @@ impl Slideshow {
}
let slide = &self.slides[self.current];
let is_dark = ui.visuals().dark_mode;
// Eşleşen slayt simgeleri için görsel belirle
let image_source = match slide.icon {
"budgie" => Some(egui::include_image!("../../assets/budgie.png")),
"cinnamon" => Some(egui::include_image!("../../assets/cinnamon.png")),
"community" => Some(egui::include_image!("../../assets/community.png")),
"file_manager" => Some(egui::include_image!("../../assets/file_manager.png")),
"gnome" => Some(egui::include_image!("../../assets/gnome.png")),
"kde" => Some(egui::include_image!("../../assets/kde.png")),
"lxqt" => Some(egui::include_image!("../../assets/lxqt.png")),
"lumina" => Some(egui::include_image!("../../assets/lumina.png")),
"lxde" => Some(egui::include_image!("../../assets/lxde.png")),
"mate" => Some(egui::include_image!("../../assets/mate.png")),
"pisilogo" => {
if is_dark {
Some(egui::include_image!("../../assets/pisi-logo-dark.png"))
} else {
Some(egui::include_image!("../../assets/pisi-logo-light.png"))
}
}
"pisi" => Some(egui::include_image!("../../assets/pisi-software-center.png")),
"pisi_speed" => Some(egui::include_image!("../../assets/pisi_speed.png")),
"securety" => Some(egui::include_image!("../../assets/securety.png")),
"xfce" => Some(egui::include_image!("../../assets/xfce.png")),
"xfce_settings" => Some(egui::include_image!("../../assets/xfce_settings.png")),
_ => None,
};
// İkona karşılık gelen URI'yi branding haritasından al
let resolved_uri = self.icons.get(slide.icon).cloned();
egui::Frame::none()
.fill(crate::ui::theme::c_bg_widget())
@@ -358,9 +349,9 @@ impl Slideshow {
ui.set_min_height(180.0);
ui.vertical_centered(|ui| {
// Simge veya Görsel
if let Some(img) = image_source {
if let Some(uri) = resolved_uri {
ui.add(
egui::Image::new(img)
egui::Image::from_uri(uri)
.max_height(64.0)
.rounding(egui::Rounding::same(8.0)),
);
BIN
View File
Binary file not shown.