Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
ximperconf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ximper Linux
ximperconf
Commits
72107231
Verified
Commit
72107231
authored
Feb 14, 2026
by
Kirill Unitsaev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
preset: refactor preset system and YAML config format
parent
5b29e43c
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
379 additions
and
405 deletions
+379
-405
config.go
config/config.go
+12
-17
presets.go
config/presets.go
+29
-40
actions.go
hyprland/actions.go
+8
-2
manager.go
hyprland/manager.go
+3
-4
var-actions.go
hyprland/var-actions.go
+1
-2
POTFILES
po/POTFILES
+2
-1
ru.po
po/ru.po
+0
-0
ximperconf.pot
po/ximperconf.pot
+110
-110
actions.go
preset/actions.go
+0
-0
commands.go
preset/commands.go
+19
-36
fileops.go
preset/fileops.go
+50
-106
helpers.go
preset/helpers.go
+58
-39
result.go
preset/result.go
+87
-8
rendertreelines.go
ui/rendertreelines.go
+0
-40
No files found.
config/config.go
View file @
72107231
...
...
@@ -3,13 +3,8 @@ package config
import
(
"os"
"path/filepath"
"ximperconf/utils"
)
type
presetEnv
struct
{
Profiles
map
[
string
]
PresetProfile
}
type
hyprEnv
struct
{
Config
string
SystemModulesDir
string
...
...
@@ -24,7 +19,7 @@ type repoEnv struct {
type
environment
struct
{
Version
string
IsRoot
bool
Pr
eset
*
presetEnv
Pr
ofiles
map
[
string
]
PresetProfile
Hyprland
*
hyprEnv
Repo
*
repoEnv
}
...
...
@@ -35,19 +30,14 @@ func InitConfig() error {
Env
.
Version
=
"0.1.0"
Env
.
IsRoot
=
os
.
Geteuid
()
==
0
pr
esetCfg
,
err
:=
loadPresetConfig
(
"/etc/ximperdistro/ximperconf/preset.d/"
)
pr
ofiles
,
err
:=
loadPresetProfiles
(
"/etc/ximperdistro/ximperconf/preset.d/"
)
if
err
!=
nil
{
// Если ошибка, пустой map
Env
.
Preset
=
&
presetEnv
{
Profiles
:
map
[
string
]
PresetProfile
{},
}
Env
.
Profiles
=
map
[
string
]
PresetProfile
{}
}
else
{
Env
.
Preset
=
&
presetEnv
{
Profiles
:
presetCfg
.
Profiles
,
}
Env
.
Profiles
=
profiles
}
if
utils
.
FileExists
(
"/usr/bin/hyprland"
)
{
if
FileExists
(
"/usr/bin/hyprland"
)
{
home
,
_
:=
os
.
UserHomeDir
()
userModules
:=
filepath
.
Join
(
home
,
".config"
,
"hypr"
)
systemModules
:=
"/etc/ximperdistro/hyprland/hypr"
...
...
@@ -63,14 +53,19 @@ func InitConfig() error {
}
Env
.
Repo
=
&
repoEnv
{
DeferredInfoURL
:
"https://download.etersoft.ru/pub/
Etersoft/Sisyphus
/Deferred_Info.html"
,
DeferredInfoURL
:
"https://download.etersoft.ru/pub/
Ximper
/Deferred_Info.html"
,
}
return
nil
}
func
FileExists
(
path
string
)
bool
{
_
,
err
:=
os
.
Stat
(
path
)
return
err
==
nil
}
func
optionalPath
(
path
string
)
string
{
if
utils
.
FileExists
(
path
)
{
if
FileExists
(
path
)
{
return
path
}
return
""
...
...
config/presets.go
View file @
72107231
...
...
@@ -9,40 +9,39 @@ import (
"gopkg.in/yaml.v3"
)
type
c
opyEntry
struct
{
type
C
opyEntry
struct
{
Src
string
`yaml:"src"`
Dest
string
`yaml:"dest"`
}
type
linkEntry
struct
{
type
LinkEntry
struct
{
Src
string
`yaml:"src"`
Dest
string
`yaml:"dest"`
Apps
[]
string
`yaml:"apps,omitempty"`
App
string
`yaml:"app,omitempty"`
Sys
string
`yaml:"sys"`
User
string
`yaml:"user"`
}
type
release
ReplaceEntry
struct
{
type
ReplaceEntry
struct
{
Src
string
`yaml:"src"`
Dest
string
`yaml:"dest"`
}
type
g
rubEntry
struct
{
type
G
rubEntry
struct
{
FixResolution
bool
`yaml:"fix-resolution,omitempty"`
Update
bool
`yaml:"update,omitempty"`
}
type
s
ystemEntry
struct
{
Grub
g
rubEntry
`yaml:"grub,omitempty"`
type
S
ystemEntry
struct
{
Grub
G
rubEntry
`yaml:"grub,omitempty"`
}
type
h
yprVar
struct
{
Var
string
`yaml:"var
"`
type
H
yprVar
struct
{
Name
string
`yaml:"name
"`
Value
string
`yaml:"value"`
Force
bool
`yaml:"force,omitempty"`
}
type
h
yprModule
struct
{
Module
string
`yaml:"modul
e"`
type
H
yprModule
struct
{
Name
string
`yaml:"nam
e"`
Action
string
`yaml:"action"`
User
bool
`yaml:"user,omitempty"`
NewOnly
bool
`yaml:"new-only,omitempty"`
...
...
@@ -51,35 +50,26 @@ type hyprModule struct {
Init
bool
`yaml:"init,omitempty"`
}
type
hyprOptions
struct
{
SyncSystemLayouts
bool
`yaml:"sync-system-layouts,omitempty"`
}
type
hyprlandEntry
struct
{
Option
hyprOptions
`yaml:"options,omitempty"`
Vars
[]
hyprVar
`yaml:"vars,omitempty"`
Modules
[]
hyprModule
`yaml:"modules,omitempty"`
type
HyprlandEntry
struct
{
SyncLayouts
bool
`yaml:"sync-layouts,omitempty"`
Vars
[]
HyprVar
`yaml:"vars,omitempty"`
Modules
[]
HyprModule
`yaml:"modules,omitempty"`
}
type
PresetProfile
struct
{
Binary
string
`yaml:"binary,omitempty"`
Description
string
`yaml:"description,omitempty"`
Root
bool
`yaml:"root,omitempty"`
Copy
[]
copyEntry
`yaml:"copy,omitempty"`
Links
[]
linkEntry
`yaml:"links,omitempty"`
ReleaseReplace
[]
releaseReplaceEntry
`yaml:"release-replace,omitempty"`
System
systemEntry
`yaml:"system,omitempty"`
// ----- hyprland -----
Hyprland
hyprlandEntry
`yaml:"hyprland,omitempty"`
Binary
string
`yaml:"binary,omitempty"`
Description
string
`yaml:"description,omitempty"`
Root
bool
`yaml:"root,omitempty"`
Priority
int
`yaml:"priority,omitempty"`
Copy
[]
CopyEntry
`yaml:"copy,omitempty"`
Links
[]
LinkEntry
`yaml:"links,omitempty"`
Replace
[]
ReplaceEntry
`yaml:"replace,omitempty"`
System
SystemEntry
`yaml:"system,omitempty"`
Hyprland
HyprlandEntry
`yaml:"hyprland,omitempty"`
}
type
Presets
struct
{
Profiles
map
[
string
]
PresetProfile
`yaml:"profiles"`
}
func
loadPresetConfig
(
dir
string
)
(
*
Presets
,
error
)
{
cfg
:=
&
Presets
{
Profiles
:
map
[
string
]
PresetProfile
{}}
func
loadPresetProfiles
(
dir
string
)
(
map
[
string
]
PresetProfile
,
error
)
{
profiles
:=
map
[
string
]
PresetProfile
{}
entries
,
err
:=
os
.
ReadDir
(
dir
)
if
err
!=
nil
{
...
...
@@ -108,9 +98,8 @@ func loadPresetConfig(dir string) (*Presets, error) {
}
profileName
:=
strings
.
TrimSuffix
(
name
,
filepath
.
Ext
(
name
))
cfg
.
Profiles
[
profileName
]
=
p
profiles
[
profileName
]
=
p
}
return
cfg
,
nil
return
profiles
,
nil
}
hyprland/actions.go
View file @
72107231
...
...
@@ -4,7 +4,6 @@ import (
"ximperconf/config"
"ximperconf/locale"
"ximperconf/ui"
"ximperconf/utils"
"context"
"errors"
...
...
@@ -267,7 +266,7 @@ func HyprlandModuleEditCommand(ctx context.Context, cmd *cli.Command) error {
return
fmt
.
Errorf
(
locale
.
T
(
"module '%s' not found: %s"
),
module
,
modulefile
)
}
editor
:=
utils
.
G
etEditor
()
editor
:=
g
etEditor
()
editCmd
:=
exec
.
Command
(
editor
,
modulefile
)
editCmd
.
Stdin
=
os
.
Stdin
...
...
@@ -344,3 +343,10 @@ func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error {
return
nil
}
func
getEditor
()
string
{
if
editor
:=
os
.
Getenv
(
"EDITOR"
);
editor
!=
""
{
return
editor
}
return
"micro"
}
hyprland/manager.go
View file @
72107231
...
...
@@ -13,7 +13,6 @@ import (
"time"
"ximperconf/config"
"ximperconf/locale"
"ximperconf/utils"
)
type
HyprlandManager
struct
{
...
...
@@ -193,10 +192,10 @@ func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
}
}
FileExists
:=
utils
.
FileExists
(
sysFile
)
FileExists
:=
config
.
FileExists
(
sysFile
)
// Конфиг Hyprland отсутствует
if
!
utils
.
FileExists
(
config
.
Env
.
Hyprland
.
Config
)
{
if
!
config
.
FileExists
(
config
.
Env
.
Hyprland
.
Config
)
{
if
FileExists
{
return
HyprModule
{
Name
:
module
,
...
...
@@ -796,7 +795,7 @@ func (m *HyprlandManager) GetPluginFile(name string) (string, error) {
}
path
:=
filepath
.
Join
(
m
.
PluginsDir
,
name
+
".so"
)
if
!
utils
.
FileExists
(
path
)
{
if
!
config
.
FileExists
(
path
)
{
return
""
,
errors
.
New
(
locale
.
T
(
"plugin not found"
))
}
...
...
hyprland/var-actions.go
View file @
72107231
...
...
@@ -5,7 +5,6 @@ import (
"ximperconf/config"
"ximperconf/locale"
"ximperconf/ui"
"ximperconf/utils"
"fmt"
"os"
...
...
@@ -121,7 +120,7 @@ func hyprlandVarUnset(name string) error {
color
.
Red
(
locale
.
T
(
"Invalid variable name: %s"
),
name
)
os
.
Exit
(
1
)
}
if
!
utils
.
FileExists
(
config
.
Env
.
Hyprland
.
Config
)
{
if
!
config
.
FileExists
(
config
.
Env
.
Hyprland
.
Config
)
{
color
.
Red
(
locale
.
T
(
"Configuration not found: %s"
),
config
.
Env
.
Hyprland
.
Config
)
os
.
Exit
(
1
)
}
...
...
po/POTFILES
View file @
72107231
...
...
@@ -7,7 +7,8 @@ hyprland/var-actions.go
main.go
preset/actions.go
preset/commands.go
preset/tools.go
preset/helpers.go
preset/result.go
repo/actions.go
repo/commands.go
system/commands.go
...
...
po/ru.po
View file @
72107231
This diff is collapsed.
Click to expand it.
po/ximperconf.pot
View file @
72107231
...
...
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: ximperconf\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-02-14
18:34
+0300\n"
"POT-Creation-Date: 2026-02-14
21:56
+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
...
...
@@ -16,6 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: hyprland/actions.go:27
msgid "Configuration fixed"
...
...
@@ -229,19 +230,19 @@ msgstr ""
msgid "toggle plugin"
msgstr ""
#: hyprland/keyboard-actions.go:4
2
#: hyprland/keyboard-actions.go:4
3
msgid "failed to get system layouts: %w"
msgstr ""
#: hyprland/keyboard-actions.go:
49
#: hyprland/keyboard-actions.go:
50
msgid "failed to update kb_layout in Hyprland: %w"
msgstr ""
#: hyprland/keyboard-actions.go:5
1
#: hyprland/keyboard-actions.go:5
2
msgid "Layout updated!"
msgstr ""
#: hyprland/keyboard-actions.go:5
3
#: hyprland/keyboard-actions.go:5
4
msgid "layout is already set, use '--force' for forced update"
msgstr ""
...
...
@@ -282,7 +283,7 @@ msgstr ""
msgid "module '%s' is already disabled"
msgstr ""
#: hyprland/manager.go:416
preset/actions.go:240 preset/actions.go:267
#: hyprland/manager.go:416
#, c-format
msgid "Module '%s' disabled"
msgstr ""
...
...
@@ -292,12 +293,12 @@ msgstr ""
msgid "module '%s' not found in config"
msgstr ""
#: hyprland/manager.go:428
preset/actions.go:242 preset/actions.go:269
#: hyprland/manager.go:428
#, c-format
msgid "Module '%s' removed"
msgstr ""
#: hyprland/manager.go:434
preset/actions.go:238 preset/actions.go:265
#: hyprland/manager.go:434
#, c-format
msgid "Module '%s' enabled"
msgstr ""
...
...
@@ -339,7 +340,7 @@ msgstr ""
msgid "Plugin '%s' unloaded"
msgstr ""
#: hyprland/manager.go:886 hyprland/manager.go:925 hyprland/var-actions.go:11
7
#: hyprland/manager.go:886 hyprland/manager.go:925 hyprland/var-actions.go:11
6
msgid "specify variable name"
msgstr ""
...
...
@@ -367,43 +368,43 @@ msgstr ""
msgid "VARS block created, variable '%s' set: %s"
msgstr ""
#: hyprland/manager.go:935 hyprland/var-actions.go:14
5
#: hyprland/manager.go:935 hyprland/var-actions.go:14
4
#, c-format
msgid "variable '%s' not found"
msgstr ""
#: hyprland/plugin-actions.go:5
3
#: hyprland/plugin-actions.go:5
4
msgid "no available plugins"
msgstr ""
#: hyprland/plugin-actions.go:8
0
#: hyprland/plugin-actions.go:8
1
msgid "Plugins"
msgstr ""
#: hyprland/var-actions.go:5
2
#: hyprland/var-actions.go:5
1
msgid "No variables in configuration"
msgstr ""
#: hyprland/var-actions.go:7
7
#: hyprland/var-actions.go:7
6
msgid "Vars"
msgstr ""
#: hyprland/var-actions.go:9
4
#: hyprland/var-actions.go:9
3
#, c-format
msgid "variable '%s' is not set"
msgstr ""
#: hyprland/var-actions.go:12
1
#: hyprland/var-actions.go:12
0
#, c-format
msgid "Invalid variable name: %s"
msgstr ""
#: hyprland/var-actions.go:12
5
#: hyprland/var-actions.go:12
4
#, c-format
msgid "Configuration not found: %s"
msgstr ""
#: hyprland/var-actions.go:14
9
#: hyprland/var-actions.go:14
8
#, c-format
msgid "Variable '%s' removed"
msgstr ""
...
...
@@ -416,194 +417,193 @@ msgstr ""
msgid "show help"
msgstr ""
#: preset/actions.go:77
#: preset/actions.go:30
msgid "copy"
msgstr ""
#: preset/actions.go:40
msgid "link"
msgstr ""
#: preset/actions.go:64
msgid "replace"
msgstr ""
#: preset/actions.go:70
#, c-format
msgid "Not found: '%s'"
msgstr ""
#: preset/actions.go:
8
5
#: preset/actions.go:
7
5
msgid "Failed to read %s: %v"
msgstr ""
#: preset/actions.go:
93
#: preset/actions.go:
79
#, c-format
msgid "Locked: %s"
msgstr ""
#: preset/actions.go:
105
#: preset/actions.go:
88
#, c-format
msgid "Up to date: '%s'"
msgstr ""
#: preset/actions.go:
120
#: preset/actions.go:
97
msgid "Replace error %s: %v"
msgstr ""
#: preset/actions.go:136 preset/tools.go:45
#: preset/actions.go:111 preset/actions.go:113 preset/actions.go:119
#: preset/actions.go:123 preset/actions.go:125
msgid "system"
msgstr ""
#: preset/actions.go:119
msgid "GRUB error: %v"
msgstr ""
#: preset/actions.go:123 system/grub-actions.go:86
msgid "GRUB_GFXMODE fixed"
msgstr ""
#: preset/actions.go:125 system/grub-actions.go:89
msgid "update-grub completed"
msgstr ""
#: preset/actions.go:133 preset/actions.go:139 preset/actions.go:144
#: preset/actions.go:146
msgid "var"
msgstr ""
#: preset/actions.go:133
#, c-format
msgid "Already exists: %s"
msgstr ""
#: preset/actions.go:1
53
#: preset/actions.go:1
44
msgid "Error setting %s: %v"
msgstr ""
#: preset/actions.go:170
msgid "Skipped module without name"
#: preset/actions.go:154 preset/actions.go:159 preset/actions.go:167
#: preset/actions.go:173 preset/actions.go:181 preset/actions.go:184
#: preset/actions.go:190 preset/actions.go:195 preset/actions.go:197
msgid "module"
msgstr ""
#: preset/actions.go:178
#, c-format
msgid "Module '%s': unsupported action"
#: preset/actions.go:154
msgid "Skipped module without name"
msgstr ""
#: preset/actions.go:1
90
#: preset/actions.go:1
59
#, c-format
msgid "Module '%s':
command '%s' failed
"
msgid "Module '%s':
unsupported action
"
msgstr ""
#: preset/actions.go:1
98
#: preset/actions.go:1
67
#, c-format
msgid "Module '%s': co
mmand '%s' returned no data
"
msgid "Module '%s': co
ndition not met
"
msgstr ""
#: preset/actions.go:
208
#: preset/actions.go:
173
#, c-format
msgid "Module '%s': binary '%s' not found"
msgstr ""
#: preset/actions.go:
220 preset/actions.go:226
#: preset/actions.go:
181 preset/actions.go:184
#, c-format
msgid "Created empty module: %s"
msgstr ""
#: preset/actions.go:
258
#: preset/actions.go:
195
msgid "Error (%s): %v"
msgstr ""
#: preset/actions.go:286
msgid "Sync Hyprland layouts"
msgstr ""
#: preset/actions.go:295
msgid "Failed to get system layouts"
#: preset/actions.go:208 preset/actions.go:214 preset/actions.go:222
#: preset/actions.go:225 preset/actions.go:227
msgid "layout"
msgstr ""
#: preset/actions.go:308
#, c-format
msgid "XCB layout: %s"
#: preset/actions.go:208
msgid "Sync Hyprland layouts"
msgstr ""
#: preset/actions.go:312
#, c-format
msgid "Hyprland layout: %s"
#: preset/actions.go:214
msgid "Failed to get system layouts"
msgstr ""
#: preset/actions.go:
319
#: preset/actions.go:
222
msgid "Failed to update kb_layout"
msgstr ""
#: preset/actions.go:
3
25
#: preset/actions.go:
2
25
#, c-format
msgid "Layout updated: %s"
msgstr ""
#: preset/actions.go:
330
#: preset/actions.go:
227
#, c-format
msgid "Layout already set: %s"
msgstr ""
#: preset/actions.go:354
msgid "GRUB error: %v"
msgstr ""
#: preset/actions.go:359 system/grub-actions.go:86
msgid "GRUB_GFXMODE fixed"
msgstr ""
#: preset/actions.go:364 system/grub-actions.go:89
msgid "update-grub completed"
msgstr ""
#: preset/actions.go:392
#: preset/actions.go:270
#, c-format
msgid "
Creating profile: %s
"
msgid "
Profile %s not found
"
msgstr ""
#: preset/actions.go:
394
#: preset/actions.go:
275
#, c-format
msgid "Profile %s unavailable"
msgstr ""
#: preset/actions.go:408 preset/actions.go:468
msgid "no available profiles"
#: preset/actions.go:287 preset/actions.go:338
#, c-format
msgid "Applying profile: %s"
msgstr ""
#: preset/actions.go:
430
msgid "
P
rofiles"
#: preset/actions.go:
321 preset/helpers.go:73
msgid "
no available p
rofiles"
msgstr ""
#: preset/actions.go:451
#, c-format
msgid "Profile %s not found"
#: preset/commands.go:13
msgid "Show what would be created without making changes"
msgstr ""
#: preset/
actions.go:472
msgid "
Creating all available profiles...
"
#: preset/
commands.go:18
msgid "
Force overwrite existing files (backup to .bak)
"
msgstr ""
#: preset/commands.go:
15
#: preset/commands.go:
26
msgid "Manage preset configuration profiles"
msgstr ""
#: preset/commands.go:
19
#: preset/commands.go:
30
msgid "Show information about preset profiles"
msgstr ""
#: preset/commands.go:
27
#: preset/commands.go:
38
msgid "Apply a profile"
msgstr ""
#: preset/commands.go:31 preset/commands.go:45
msgid "Show what would be created without making changes"
msgstr ""
#: preset/commands.go:41
#: preset/commands.go:45
msgid "Apply all available profiles"
msgstr ""
#: preset/tools.go:51
#, c-format
msgid "Error: %s"
msgstr ""
#: preset/tools.go:85
msgid "Copying"
msgstr ""
#: preset/tools.go:86
msgid "Creating links"
msgstr ""
#: preset/tools.go:87
msgid "Updating"
msgstr ""
#: preset/tools.go:88
msgid "Configuring system"
#: preset/helpers.go:93
msgid "Profiles"
msgstr ""
#: preset/
tools.go:89
msgid "
Creating Hyprland variables
"
#: preset/
result.go:74
msgid "
done
"
msgstr ""
#: preset/
tools.go:90
msgid "
Enabling Hyprland modules
"
#: preset/
result.go:77
msgid "
skipped
"
msgstr ""
#: preset/tools.go:91
msgid "Syncing Hyprland layout"
msgstr ""
#: preset/result.go:80
msgid "error"
msgid_plural "errors"
msgstr[0] ""
msgstr[1] ""
#: repo/actions.go:31 repo/actions.go:38
msgid "Error fetching data"
...
...
preset/actions.go
View file @
72107231
This diff is collapsed.
Click to expand it.
preset/commands.go
View file @
72107231
package
preset
import
(
"context"
"fmt"
"ximperconf/config"
"ximperconf/locale"
"github.com/urfave/cli/v3"
)
var
presetFlags
=
[]
cli
.
Flag
{
&
cli
.
BoolFlag
{
Name
:
"dry-run"
,
Usage
:
locale
.
T
(
"Show what would be created without making changes"
),
Aliases
:
[]
string
{
"d"
},
},
&
cli
.
BoolFlag
{
Name
:
"force"
,
Usage
:
locale
.
T
(
"Force overwrite existing files (backup to .bak)"
),
Aliases
:
[]
string
{
"f"
},
},
}
func
CommandList
()
*
cli
.
Command
{
return
&
cli
.
Command
{
Name
:
"preset"
,
...
...
@@ -23,46 +34,18 @@ func CommandList() *cli.Command {
Action
:
presetInfoCommand
,
},
{
Name
:
"apply"
,
Usage
:
locale
.
T
(
"Apply a profile"
),
Flags
:
[]
cli
.
Flag
{
&
cli
.
BoolFlag
{
Name
:
"dry-run"
,
Usage
:
locale
.
T
(
"Show what would be created without making changes"
),
Aliases
:
[]
string
{
"d"
},
Value
:
false
,
},
},
Name
:
"apply"
,
Usage
:
locale
.
T
(
"Apply a profile"
),
Flags
:
presetFlags
,
Action
:
presetApplyCommand
,
ShellComplete
:
ShellCompleteProfiles
,
},
{
Name
:
"apply-all"
,
Usage
:
locale
.
T
(
"Apply all available profiles"
),
Flags
:
[]
cli
.
Flag
{
&
cli
.
BoolFlag
{
Name
:
"dry-run"
,
Usage
:
locale
.
T
(
"Show what would be created without making changes"
),
Aliases
:
[]
string
{
"d"
},
Value
:
false
,
},
},
Name
:
"apply-all"
,
Usage
:
locale
.
T
(
"Apply all available profiles"
),
Flags
:
presetFlags
,
Action
:
presetApplyAllCommand
,
},
},
}
}
func
ShellCompleteProfiles
(
ctx
context
.
Context
,
cmd
*
cli
.
Command
)
{
if
cmd
.
NArg
()
>
0
{
return
}
profiles
:=
config
.
Env
.
Preset
.
Profiles
for
profileName
,
profile
:=
range
profiles
{
if
profileAvailable
(
profile
)
{
fmt
.
Println
(
profileName
)
}
}
}
preset/files.go
→
preset/file
op
s.go
View file @
72107231
...
...
@@ -2,67 +2,35 @@ package preset
import
(
"os"
"os/exec"
"path/filepath"
"strings"
"ximperconf/config"
)
type
opKind
string
const
(
OpCopy
opKind
=
"copy"
OpLink
opKind
=
"link"
OpReplace
opKind
=
"replace"
)
type
opOptions
struct
{
DryRun
bool
Force
bool
}
type
opResult
struct
{
Kind
opKind
Status
config
.
ItemStatus
Source
string
Target
string
Reason
string
}
func
commandExists
(
name
string
)
bool
{
_
,
err
:=
exec
.
LookPath
(
name
)
return
err
==
nil
}
// ===== Copy =====
func
copyIfMissing
(
src
,
dest
string
,
opts
opOptions
)
opResult
{
func
execCopy
(
src
,
dest
string
,
opts
opOptions
)
(
config
.
ItemStatus
,
string
)
{
info
,
err
:=
os
.
Stat
(
src
)
if
err
!=
nil
{
return
opResult
{
Kind
:
OpCopy
,
Status
:
config
.
OpStatus
.
Error
,
Source
:
src
,
Target
:
dest
,
Reason
:
err
.
Error
(),
}
return
config
.
OpStatus
.
Error
,
err
.
Error
()
}
exists
:=
false
if
_
,
err
:=
os
.
Stat
(
dest
);
err
==
nil
{
return
opResult
{
Kind
:
OpCopy
,
Status
:
config
.
OpStatus
.
Skipped
,
Target
:
dest
,
Reason
:
"already exists"
,
if
!
opts
.
Force
{
return
config
.
OpStatus
.
Skipped
,
dest
+
" (exists)"
}
exists
=
true
if
!
opts
.
DryRun
{
_
=
os
.
Rename
(
dest
,
dest
+
".bak"
)
}
}
if
opts
.
DryRun
{
return
opResult
{
Kind
:
OpCopy
,
Status
:
config
.
OpStatus
.
DryRun
,
Source
:
src
,
Target
:
dest
,
}
return
config
.
OpStatus
.
DryRun
,
src
+
" → "
+
dest
}
if
info
.
IsDir
()
{
...
...
@@ -72,21 +40,49 @@ func copyIfMissing(src, dest string, opts opOptions) opResult {
}
if
err
!=
nil
{
return
opResult
{
Kind
:
OpCopy
,
Status
:
config
.
OpStatus
.
Error
,
Source
:
src
,
Target
:
dest
,
Reason
:
err
.
Error
(),
return
config
.
OpStatus
.
Error
,
err
.
Error
()
}
msg
:=
src
+
" → "
+
dest
if
exists
{
msg
+=
" (overwritten)"
}
return
config
.
OpStatus
.
Done
,
msg
}
func
execLink
(
src
,
dest
string
,
opts
opOptions
)
(
config
.
ItemStatus
,
string
)
{
if
_
,
err
:=
os
.
Stat
(
src
);
os
.
IsNotExist
(
err
)
{
return
config
.
OpStatus
.
Skipped
,
src
+
" → "
+
dest
+
" (source missing)"
}
exists
:=
false
if
_
,
err
:=
os
.
Lstat
(
dest
);
err
==
nil
{
if
!
opts
.
Force
{
return
config
.
OpStatus
.
Skipped
,
dest
+
" (exists)"
}
exists
=
true
if
!
opts
.
DryRun
{
_
=
os
.
Rename
(
dest
,
dest
+
".bak"
)
}
}
return
opResult
{
Kind
:
OpCopy
,
Status
:
config
.
OpStatus
.
Done
,
Source
:
src
,
Target
:
dest
,
if
opts
.
DryRun
{
return
config
.
OpStatus
.
DryRun
,
src
+
" → "
+
dest
}
if
err
:=
os
.
MkdirAll
(
filepath
.
Dir
(
dest
),
0755
);
err
!=
nil
{
return
config
.
OpStatus
.
Error
,
err
.
Error
()
}
if
err
:=
os
.
Symlink
(
src
,
dest
);
err
!=
nil
{
return
config
.
OpStatus
.
Error
,
err
.
Error
()
}
msg
:=
src
+
" → "
+
dest
if
exists
{
msg
+=
" (overwritten)"
}
return
config
.
OpStatus
.
Done
,
msg
}
func
copyFile
(
src
,
dest
string
)
error
{
...
...
@@ -121,60 +117,8 @@ func copyDir(src, dest string) error {
})
}
// ===== Link =====
func
createLinkIfMissing
(
src
,
dest
string
,
opts
opOptions
)
opResult
{
if
_
,
err
:=
os
.
Stat
(
src
);
os
.
IsNotExist
(
err
)
{
return
opResult
{
Kind
:
OpLink
,
Status
:
config
.
OpStatus
.
Skipped
,
Target
:
dest
,
Reason
:
"source missing"
,
}
}
if
_
,
err
:=
os
.
Lstat
(
dest
);
err
==
nil
{
return
opResult
{
Kind
:
OpLink
,
Status
:
config
.
OpStatus
.
Skipped
,
Target
:
dest
,
Reason
:
"already exists"
,
}
}
if
opts
.
DryRun
{
return
opResult
{
Kind
:
OpLink
,
Status
:
config
.
OpStatus
.
DryRun
,
Source
:
src
,
Target
:
dest
,
}
}
if
err
:=
os
.
MkdirAll
(
filepath
.
Dir
(
dest
),
0755
);
err
!=
nil
{
return
opResult
{
Kind
:
OpLink
,
Status
:
config
.
OpStatus
.
Error
,
Reason
:
err
.
Error
(),
}
}
if
err
:=
os
.
Symlink
(
src
,
dest
);
err
!=
nil
{
return
opResult
{
Kind
:
OpLink
,
Status
:
config
.
OpStatus
.
Error
,
Reason
:
err
.
Error
(),
}
}
return
opResult
{
Kind
:
OpLink
,
Status
:
config
.
OpStatus
.
Done
,
Source
:
src
,
Target
:
dest
,
}
}
// ===== Versions =====
func
getSystemVersion
()
string
{
data
,
err
:=
os
.
ReadFile
(
"/etc/os-release"
)
if
err
!=
nil
{
...
...
preset/
tool
s.go
→
preset/
helper
s.go
View file @
72107231
package
preset
import
(
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"ximperconf/config"
"ximperconf/locale"
"ximperconf/ui"
"github.com/urfave/cli/v3"
)
func
profileAvailable
(
p
config
.
PresetProfile
)
bool
{
allowed
:=
false
if
p
.
Binary
==
""
{
allowed
=
true
}
else
if
_
,
err
:=
exec
.
LookPath
(
p
.
Binary
);
err
==
nil
{
allowed
=
true
if
p
.
Binary
!=
""
{
if
_
,
err
:=
exec
.
LookPath
(
p
.
Binary
);
err
!=
nil
{
return
false
}
}
if
p
.
Root
!=
config
.
Env
.
IsRoot
{
allowed
=
false
return
false
}
return
allowed
return
true
}
func
profileStatus
(
p
config
.
PresetProfile
)
config
.
ItemStatus
{
...
...
@@ -35,28 +35,9 @@ func profileStatus(p config.PresetProfile) config.ItemStatus {
return
config
.
ProfileStatus
.
Unavailable
}
func
AppendOpResult
(
res
*
Result
,
r
opResult
)
{
item
:=
ui
.
TreeItem
{
Status
:
r
.
Status
,
}
switch
{
case
r
.
Status
.
IsEqual
(
config
.
OpStatus
.
Skipped
)
:
item
.
Name
=
fmt
.
Sprintf
(
locale
.
T
(
"Already exists: %s"
),
r
.
Target
)
case
r
.
Status
.
IsEqual
(
config
.
OpStatus
.
DryRun
)
:
item
.
Name
=
fmt
.
Sprintf
(
"%s → %s"
,
r
.
Source
,
r
.
Target
)
case
r
.
Status
.
IsEqual
(
config
.
OpStatus
.
Done
)
:
item
.
Name
=
fmt
.
Sprintf
(
"%s → %s"
,
r
.
Source
,
r
.
Target
)
case
r
.
Status
.
IsEqual
(
config
.
OpStatus
.
Error
)
:
item
.
Name
=
fmt
.
Sprintf
(
locale
.
T
(
"Error: %s"
),
r
.
Reason
)
}
switch
r
.
Kind
{
case
OpCopy
:
res
.
Copies
=
append
(
res
.
Copies
,
item
)
case
OpLink
:
res
.
Links
=
append
(
res
.
Links
,
item
)
}
func
commandExists
(
name
string
)
bool
{
_
,
err
:=
exec
.
LookPath
(
name
)
return
err
==
nil
}
func
expandPath
(
path
,
name
string
)
string
{
...
...
@@ -81,12 +62,50 @@ func expandPath(path, name string) string {
return
path
}
func
renderPresetResult
(
res
*
Result
)
{
ui
.
RenderTreeItems
(
locale
.
T
(
"Copying"
),
res
.
Copies
)
ui
.
RenderTreeItems
(
locale
.
T
(
"Creating links"
),
res
.
Links
)
ui
.
RenderTreeItems
(
locale
.
T
(
"Updating"
),
res
.
Replaced
)
ui
.
RenderTreeItems
(
locale
.
T
(
"Configuring system"
),
res
.
System
)
ui
.
RenderTreeItems
(
locale
.
T
(
"Creating Hyprland variables"
),
res
.
HyprVars
)
ui
.
RenderTreeItems
(
locale
.
T
(
"Enabling Hyprland modules"
),
res
.
HyprModules
)
ui
.
RenderTreeItems
(
locale
.
T
(
"Syncing Hyprland layout"
),
res
.
SyncSystemLayouts
)
func
ShowProfilesInfo
(
jsonFormat
bool
)
{
profiles
:=
config
.
Env
.
Profiles
if
len
(
profiles
)
==
0
{
if
jsonFormat
{
ui
.
PrintJSON
([]
ui
.
JSONItem
{})
return
}
fmt
.
Println
(
config
.
OpStatus
.
Error
.
Color
(
locale
.
T
(
"no available profiles"
)))
return
}
items
:=
make
([]
ui
.
TreeItem
,
0
,
len
(
profiles
))
for
name
,
profile
:=
range
profiles
{
items
=
append
(
items
,
ui
.
TreeItem
{
Name
:
name
,
Status
:
profileStatus
(
profile
),
Description
:
profile
.
Description
,
})
}
if
jsonFormat
{
ui
.
PrintJSON
(
ui
.
TreeItemsToJSON
(
items
))
return
}
ui
.
RenderTree
(
ui
.
RenderTreeOptions
{
Title
:
locale
.
T
(
"Profiles"
),
Items
:
items
,
Style
:
ui
.
DefaultTreeStyle
,
Color
:
true
,
Sort
:
true
,
})
}
func
ShellCompleteProfiles
(
ctx
context
.
Context
,
cmd
*
cli
.
Command
)
{
if
cmd
.
NArg
()
>
0
{
return
}
for
name
,
profile
:=
range
config
.
Env
.
Profiles
{
if
profileAvailable
(
profile
)
{
fmt
.
Println
(
name
)
}
}
}
preset/result.go
View file @
72107231
package
preset
import
"ximperconf/ui"
import
(
"fmt"
"ximperconf/config"
"ximperconf/locale"
)
type
LogEntry
struct
{
Category
string
Name
string
Status
config
.
ItemStatus
}
type
Result
struct
{
Copies
[]
ui
.
TreeItem
Links
[]
ui
.
TreeItem
Replaced
[]
ui
.
TreeItem
System
[]
ui
.
TreeItem
HyprVars
[]
ui
.
TreeItem
HyprModules
[]
ui
.
TreeItem
SyncSystemLayouts
[]
ui
.
TreeItem
entries
[]
LogEntry
}
func
(
r
*
Result
)
Add
(
category
,
name
string
,
status
config
.
ItemStatus
)
{
r
.
entries
=
append
(
r
.
entries
,
LogEntry
{
Category
:
category
,
Name
:
name
,
Status
:
status
,
})
}
func
(
r
*
Result
)
Render
()
{
if
len
(
r
.
entries
)
==
0
{
return
}
maxCat
:=
0
for
_
,
e
:=
range
r
.
entries
{
if
len
([]
rune
(
e
.
Category
))
>
maxCat
{
maxCat
=
len
([]
rune
(
e
.
Category
))
}
}
for
_
,
e
:=
range
r
.
entries
{
symbol
:=
e
.
Status
.
Symbol
cat
:=
fmt
.
Sprintf
(
"%-*s"
,
maxCat
,
e
.
Category
)
if
e
.
Status
.
Color
!=
nil
{
symbol
=
e
.
Status
.
Color
(
symbol
)
cat
=
e
.
Status
.
Color
(
cat
)
}
fmt
.
Printf
(
" %s %s %s
\n
"
,
symbol
,
cat
,
e
.
Name
)
}
summary
:=
r
.
Summary
()
if
summary
!=
""
{
fmt
.
Println
()
fmt
.
Printf
(
" %s
\n
"
,
summary
)
}
}
func
(
r
*
Result
)
Summary
()
string
{
var
done
,
skipped
,
errors
int
for
_
,
e
:=
range
r
.
entries
{
switch
{
case
e
.
Status
.
IsEqual
(
config
.
OpStatus
.
Done
)
:
done
++
case
e
.
Status
.
IsEqual
(
config
.
OpStatus
.
Skipped
)
:
skipped
++
case
e
.
Status
.
IsEqual
(
config
.
OpStatus
.
Error
)
:
errors
++
case
e
.
Status
.
IsEqual
(
config
.
OpStatus
.
DryRun
)
:
done
++
}
}
parts
:=
[]
string
{}
if
done
>
0
{
parts
=
append
(
parts
,
fmt
.
Sprintf
(
"%d %s"
,
done
,
locale
.
T
(
"done"
)))
}
if
skipped
>
0
{
parts
=
append
(
parts
,
fmt
.
Sprintf
(
"%d %s"
,
skipped
,
locale
.
T
(
"skipped"
)))
}
if
errors
>
0
{
parts
=
append
(
parts
,
fmt
.
Sprintf
(
"%d %s"
,
errors
,
locale
.
TN
(
"error"
,
"errors"
,
errors
)))
}
if
len
(
parts
)
==
0
{
return
""
}
result
:=
parts
[
0
]
for
i
:=
1
;
i
<
len
(
parts
);
i
++
{
result
+=
", "
+
parts
[
i
]
}
return
result
}
ui/rendertreelines.go
deleted
100644 → 0
View file @
5b29e43c
package
ui
import
(
"github.com/fatih/color"
)
func
RenderTreeLines
(
title
string
,
lines
[]
string
)
{
if
len
(
lines
)
==
0
{
return
}
color
.
Blue
(
"%s"
,
title
)
items
:=
make
([]
TreeItem
,
len
(
lines
))
for
i
,
line
:=
range
lines
{
items
[
i
]
=
TreeItem
{
Name
:
line
,
}
}
RenderTree
(
RenderTreeOptions
{
Items
:
items
,
Style
:
DefaultTreeStyle
,
Color
:
false
,
})
}
func
RenderTreeItems
(
title
string
,
items
[]
TreeItem
)
{
if
len
(
items
)
==
0
{
return
}
color
.
Blue
(
"%s"
,
title
)
RenderTree
(
RenderTreeOptions
{
Items
:
items
,
Style
:
DefaultTreeStyle
,
Color
:
true
,
})
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment