Комментирование, правки кода, исправление модальных окон
This commit is contained in:
parent
2e8eee135e
commit
300afb5d26
8 changed files with 299 additions and 618 deletions
436
log.txt
436
log.txt
|
|
@ -1,436 +0,0 @@
|
||||||
time=13:34:29 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=13:34:29 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=13:34:30 29-12-2025 level=info msg=sections user, focus 0, fields len 2
|
|
||||||
time=13:34:30 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:34:30 29-12-2025 level=info msg=field choose_user, capture keys true
|
|
||||||
time=13:34:30 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:34:30 29-12-2025 level=info msg=field choose_user, capture keys true
|
|
||||||
time=13:34:31 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:34:31 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=13:34:32 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=13:34:32 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=13:34:32 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=13:34:32 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:34:32 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=13:34:32 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=13:34:32 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=13:34:33 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=13:34:33 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=13:34:33 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=13:34:33 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=13:34:33 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=13:34:33 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=13:34:34 29-12-2025 level=info msg=sections domain, focus 5, fields len 5
|
|
||||||
time=13:34:34 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:34:34 29-12-2025 level=info msg=field database_type, capture keys false
|
|
||||||
time=13:34:35 29-12-2025 level=info msg=sections database, focus 1, fields len 1
|
|
||||||
time=13:34:36 29-12-2025 level=info msg=sections paths, focus 0, fields len 1
|
|
||||||
time=13:34:37 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:34:37 29-12-2025 level=info msg=field debian_root, capture keys true
|
|
||||||
time=13:34:37 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:34:37 29-12-2025 level=info msg=field debian_root, capture keys true
|
|
||||||
time=13:34:37 29-12-2025 level=info msg=sections paths, focus 0, fields len 1
|
|
||||||
time=13:54:35 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=13:54:37 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=13:54:38 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:54:38 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=13:54:38 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=13:54:38 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=13:54:39 29-12-2025 level=info msg=2
|
|
||||||
time=13:54:39 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=13:54:39 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=13:54:40 29-12-2025 level=info msg=sections user, focus 0, fields len 2
|
|
||||||
time=13:54:40 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:54:40 29-12-2025 level=info msg=field choose_user, capture keys true
|
|
||||||
time=13:54:41 29-12-2025 level=info msg=sections user, focus 0, fields len 2
|
|
||||||
time=13:54:41 29-12-2025 level=info msg=chose {FieldIndex:1 Value:Existing}
|
|
||||||
time=13:54:42 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:54:42 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=13:54:42 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=13:54:42 29-12-2025 level=info msg=field select_user, capture keys false
|
|
||||||
time=13:54:42 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=13:54:42 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:54:42 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=13:54:43 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=13:54:43 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=13:54:43 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=13:54:43 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=13:54:43 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=13:54:43 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=13:54:43 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=13:54:43 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=13:54:44 29-12-2025 level=info msg=sections domain, focus 5, fields len 5
|
|
||||||
time=13:54:44 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:54:44 29-12-2025 level=info msg=field database_type, capture keys false
|
|
||||||
time=13:54:44 29-12-2025 level=info msg=sections database, focus 1, fields len 1
|
|
||||||
time=13:54:45 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=13:54:45 29-12-2025 level=info msg=field debian_root, capture keys false
|
|
||||||
time=13:54:45 29-12-2025 level=info msg=sections paths, focus 1, fields len 1
|
|
||||||
time=13:57:35 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:00:04 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:08:24 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:08:25 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=14:08:25 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:08:25 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=14:08:26 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:08:26 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:08:26 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=14:08:28 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:08:28 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:28:24 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:28:25 29-12-2025 level=info msg=2
|
|
||||||
time=14:28:25 29-12-2025 level=info msg=2
|
|
||||||
time=14:28:25 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=14:28:26 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:28:26 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=14:28:26 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:28:26 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:28:27 29-12-2025 level=info msg=2
|
|
||||||
time=14:28:27 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:28:27 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:28:28 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:28:28 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=14:28:29 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:28:29 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:28:29 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:28:29 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=14:28:29 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:28:29 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:28:49 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:28:50 29-12-2025 level=info msg=2
|
|
||||||
time=14:28:50 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=14:28:50 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:28:50 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=14:28:51 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:28:51 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:28:52 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=14:28:52 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:28:52 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:28:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:28:53 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:28:53 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:28:53 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=2
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:29:53 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:29:54 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:29:54 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:29:54 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:29:54 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:29:54 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:29:54 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:30:06 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:30:08 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=14:31:23 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:31:23 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=14:31:23 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:31:23 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:31:24 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=14:32:32 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:38:03 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:38:04 29-12-2025 level=info msg=2
|
|
||||||
time=14:38:04 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=14:38:05 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:38:05 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=14:38:05 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:38:05 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:38:06 29-12-2025 level=info msg=2
|
|
||||||
time=14:38:06 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:38:06 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:38:07 29-12-2025 level=info msg=2
|
|
||||||
time=14:38:07 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:38:07 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:38:08 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=14:38:08 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:38:08 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:38:09 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:38:09 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:38:09 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:38:09 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:38:09 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:38:09 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:38:10 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:38:10 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:39:11 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:39:11 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=14:39:52 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:40:10 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:40:12 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=14:40:31 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=14:40:32 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=14:40:32 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:40:32 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=14:40:32 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:40:32 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=14:40:33 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=14:40:33 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:40:33 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:40:33 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:40:33 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:40:34 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:40:34 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:40:34 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:40:34 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:40:34 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:40:34 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:40:34 29-12-2025 level=info msg=2
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:41:48 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:41:49 29-12-2025 level=info msg=sections domain, focus 3, fields len 5
|
|
||||||
time=14:41:49 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=14:41:49 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=14:41:49 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=14:41:49 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=14:41:49 29-12-2025 level=info msg=sections domain, focus 5, fields len 5
|
|
||||||
time=14:41:52 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=14:41:52 29-12-2025 level=info msg=field ssl_type, capture keys false
|
|
||||||
time=14:41:53 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:41:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:41:53 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:41:53 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:41:53 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:41:54 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:41:54 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:41:54 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:41:55 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:41:56 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:41:57 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:41:58 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:41:58 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:41:58 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:41:58 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:41:59 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:41:59 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:41:59 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:00 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:00 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:00 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:00 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:00 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:00 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:00 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:00 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:01 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:01 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:01 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:01 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:01 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:01 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:01 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:01 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:02 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:02 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:02 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:42:03 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:03 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:03 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:03 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:03 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:03 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:03 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:03 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:04 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:04 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:04 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:04 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:04 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:04 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:05 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:42:05 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:05 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:05 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:05 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:05 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:05 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:06 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:06 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:06 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:06 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:06 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:06 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:06 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:06 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:07 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:07 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:07 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:07 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:07 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:08 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:09 29-12-2025 level=info msg=sections ssl, focus 1, fields len 3
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:10 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:11 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=14:42:12 29-12-2025 level=info msg=field ssl_cert_path, capture keys true
|
|
||||||
time=14:42:16 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=15:05:11 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=15:05:12 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=15:17:37 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=15:24:20 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=16:26:13 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=16:26:13 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=16:26:14 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=16:26:14 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=16:26:14 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=16:26:14 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=16:26:58 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=16:49:25 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=17:01:58 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=17:04:53 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=17:04:54 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:04:54 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=17:04:54 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=17:04:54 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=17:04:54 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:04:55 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=17:04:56 29-12-2025 level=info msg=sections domain, focus 5, fields len 5
|
|
||||||
time=17:04:56 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:04:56 29-12-2025 level=info msg=field database_type, capture keys false
|
|
||||||
time=17:04:57 29-12-2025 level=info msg=sections database, focus 1, fields len 1
|
|
||||||
time=17:04:57 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:04:57 29-12-2025 level=info msg=field debian_root, capture keys false
|
|
||||||
time=17:04:57 29-12-2025 level=info msg=sections paths, focus 1, fields len 1
|
|
||||||
time=17:43:36 29-12-2025 level=info msg=modal close msg get
|
|
||||||
time=17:43:37 29-12-2025 level=info msg=sections Repko about, focus 0, fields len 0
|
|
||||||
time=17:43:38 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:43:38 29-12-2025 level=info msg=field choose_user, capture keys false
|
|
||||||
time=17:43:38 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=17:43:38 29-12-2025 level=info msg=field new_user, capture keys false
|
|
||||||
time=17:43:39 29-12-2025 level=info msg=sections user, focus 2, fields len 2
|
|
||||||
time=17:43:39 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:43:39 29-12-2025 level=info msg=field api_domain, capture keys false
|
|
||||||
time=17:43:39 29-12-2025 level=info msg=1, focus is 1
|
|
||||||
time=17:43:39 29-12-2025 level=info msg=field panel_domain, capture keys false
|
|
||||||
time=17:43:39 29-12-2025 level=info msg=1, focus is 2
|
|
||||||
time=17:43:39 29-12-2025 level=info msg=field cookie_domain, capture keys false
|
|
||||||
time=17:43:40 29-12-2025 level=info msg=1, focus is 3
|
|
||||||
time=17:43:40 29-12-2025 level=info msg=field is_secure, capture keys false
|
|
||||||
time=17:43:40 29-12-2025 level=info msg=1, focus is 4
|
|
||||||
time=17:43:40 29-12-2025 level=info msg=field server_address, capture keys false
|
|
||||||
time=17:43:40 29-12-2025 level=info msg=sections domain, focus 5, fields len 5
|
|
||||||
time=17:43:40 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:43:40 29-12-2025 level=info msg=field database_type, capture keys false
|
|
||||||
time=17:43:41 29-12-2025 level=info msg=sections database, focus 1, fields len 1
|
|
||||||
time=17:43:41 29-12-2025 level=info msg=1, focus is 0
|
|
||||||
time=17:43:41 29-12-2025 level=info msg=field debian_root, capture keys false
|
|
||||||
time=17:43:41 29-12-2025 level=info msg=sections paths, focus 1, fields len 1
|
|
||||||
|
|
@ -155,6 +155,7 @@ func ComputeValue(computeExpr string, app *types.PackageApplication) (interface{
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recompute производит перерасчет всех полей
|
||||||
func Recompute(app *types.PackageApplication) {
|
func Recompute(app *types.PackageApplication) {
|
||||||
for i := range app.Sections {
|
for i := range app.Sections {
|
||||||
section := &app.Sections[i]
|
section := &app.Sections[i]
|
||||||
|
|
@ -162,7 +163,8 @@ func Recompute(app *types.PackageApplication) {
|
||||||
field := §ion.Fields[j]
|
field := §ion.Fields[j]
|
||||||
if field.FieldType == "compute" && field.Compute != "" {
|
if field.FieldType == "compute" && field.Compute != "" {
|
||||||
if field.Variable != nil {
|
if field.Variable != nil {
|
||||||
continue //?почему мы пропускаем
|
// todo почему мы пропускаем?
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
value, err := ComputeValue(field.Compute, app)
|
value, err := ComputeValue(field.Compute, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -174,8 +176,10 @@ func Recompute(app *types.PackageApplication) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefineExprCustomFunctions возвращает собственные функции expr
|
||||||
func DefineExprCustomFunctions() []expr.Option {
|
func DefineExprCustomFunctions() []expr.Option {
|
||||||
return []expr.Option{
|
return []expr.Option{
|
||||||
|
//вычисляет значение по заданному регулярному выражению
|
||||||
expr.Function("regex",
|
expr.Function("regex",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
|
|
@ -191,6 +195,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
return re.ReplaceAllString(input, repl), nil
|
return re.ReplaceAllString(input, repl), nil
|
||||||
}, new(func(string, string, string) string),
|
}, new(func(string, string, string) string),
|
||||||
),
|
),
|
||||||
|
//проверяет совпадение переданного значения и указанного регулярного выражения
|
||||||
expr.Function("regex_match",
|
expr.Function("regex_match",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
|
|
@ -207,6 +212,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
|
|
||||||
}, new(func(string, string) bool),
|
}, new(func(string, string) bool),
|
||||||
),
|
),
|
||||||
|
//возвращает собственный текст ошибки
|
||||||
expr.Function("error",
|
expr.Function("error",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -216,6 +222,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
},
|
},
|
||||||
new(func(string) error),
|
new(func(string) error),
|
||||||
),
|
),
|
||||||
|
//возвращает тело ответа по указанному адресу
|
||||||
expr.Function("http_get",
|
expr.Function("http_get",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -232,6 +239,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
},
|
},
|
||||||
new(func(string) string),
|
new(func(string) string),
|
||||||
),
|
),
|
||||||
|
//возвращает результат выполнения команды из оболочки
|
||||||
expr.Function("shell",
|
expr.Function("shell",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -250,6 +258,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
},
|
},
|
||||||
new(func(string) error),
|
new(func(string) error),
|
||||||
),
|
),
|
||||||
|
//возвращает значение указанной переменной из env
|
||||||
expr.Function("env",
|
expr.Function("env",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -258,6 +267,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
return os.Getenv(fmt.Sprintf("%v", args[0])), nil
|
return os.Getenv(fmt.Sprintf("%v", args[0])), nil
|
||||||
}, new(func(string) error),
|
}, new(func(string) error),
|
||||||
),
|
),
|
||||||
|
//проверяет наличие файла в системе
|
||||||
expr.Function("file_exists",
|
expr.Function("file_exists",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -268,6 +278,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
return err == nil, nil
|
return err == nil, nil
|
||||||
}, new(func(string) bool),
|
}, new(func(string) bool),
|
||||||
),
|
),
|
||||||
|
//проверяет наличие директории в системе
|
||||||
expr.Function("dir_exists",
|
expr.Function("dir_exists",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -284,6 +295,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
return false, nil
|
return false, nil
|
||||||
}, new(func(string) bool),
|
}, new(func(string) bool),
|
||||||
),
|
),
|
||||||
|
//проверяет доступность порта
|
||||||
expr.Function("port_free",
|
expr.Function("port_free",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -294,6 +306,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
return cmd.Run() != nil, nil
|
return cmd.Run() != nil, nil
|
||||||
}, new(func(string) error),
|
}, new(func(string) error),
|
||||||
),
|
),
|
||||||
|
//проверяет наличие пользователя в системе
|
||||||
expr.Function("user_exists",
|
expr.Function("user_exists",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
@ -307,6 +320,7 @@ func DefineExprCustomFunctions() []expr.Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpecialValueHandle получаем необычные значения полей
|
||||||
func SpecialValueHandle(special string) interface{} {
|
func SpecialValueHandle(special string) interface{} {
|
||||||
specialValue := strings.Split(special, "code:")[1]
|
specialValue := strings.Split(special, "code:")[1]
|
||||||
switch specialValue {
|
switch specialValue {
|
||||||
|
|
|
||||||
|
|
@ -10,21 +10,21 @@ import (
|
||||||
"github.com/charmbracelet/lipgloss/table"
|
"github.com/charmbracelet/lipgloss/table"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FinalModel структур описывающая модель отрисовки таблицы переменных
|
||||||
type FinalModel struct {
|
type FinalModel struct {
|
||||||
variables []types.Variable
|
variables []types.Variable //Список переменных
|
||||||
table *table.Table
|
table *table.Table //Таблица для вывода
|
||||||
viewport viewport.Model
|
viewport viewport.Model //Объект куда помещена таблица для возможности прокручивать её
|
||||||
ready bool
|
width int //Ширина окна
|
||||||
width int
|
height int //Высота окна
|
||||||
height int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFinalModel создается новую модель таблицы переменных
|
||||||
func NewFinalModel(variables []types.Variable, width, height int) *FinalModel {
|
func NewFinalModel(variables []types.Variable, width, height int) *FinalModel {
|
||||||
m := &FinalModel{
|
m := &FinalModel{
|
||||||
variables: variables,
|
variables: variables,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
ready: false,
|
|
||||||
}
|
}
|
||||||
if width > 0 && height > 0 {
|
if width > 0 && height > 0 {
|
||||||
m.initViewport(width, height)
|
m.initViewport(width, height)
|
||||||
|
|
@ -36,60 +36,70 @@ func (m *FinalModel) Init() tea.Cmd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update отслеживает действия со таблицей
|
||||||
func (m *FinalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *FinalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
|
//Отслеживаем нажатие клавиш
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "up":
|
case "up":
|
||||||
|
//Обработчик up для прокручивания вверх на 1 позицию
|
||||||
m.viewport.ScrollUp(1)
|
m.viewport.ScrollUp(1)
|
||||||
case "down":
|
case "down":
|
||||||
|
//Обработчик down для прокручивания вниз на 1 позицию
|
||||||
m.viewport.ScrollDown(1)
|
m.viewport.ScrollDown(1)
|
||||||
case "left":
|
case "left":
|
||||||
|
//Обработчик left для прокручивания влево на 1 позицию
|
||||||
m.viewport.ScrollLeft(1)
|
m.viewport.ScrollLeft(1)
|
||||||
case "right":
|
case "right":
|
||||||
|
//Обработчик right для прокручивания вправо на 1 позицию
|
||||||
m.viewport.ScrollRight(1)
|
m.viewport.ScrollRight(1)
|
||||||
case "pageup":
|
case "pageup":
|
||||||
|
//Обработчик pageup для прокручивания вверх на половину допустимой высоты viewport
|
||||||
m.viewport.HalfPageUp()
|
m.viewport.HalfPageUp()
|
||||||
case "pagedown":
|
case "pagedown":
|
||||||
|
//Обработчик pagedown для прокручивания вниз на половину допустимой высоты viewport
|
||||||
m.viewport.HalfPageDown()
|
m.viewport.HalfPageDown()
|
||||||
case "home":
|
case "home":
|
||||||
|
//Обработчик home для перехода в самый вверх
|
||||||
m.viewport.GotoTop()
|
m.viewport.GotoTop()
|
||||||
case "end":
|
case "end":
|
||||||
|
//Обработчик end для перехода в самый низ
|
||||||
m.viewport.GotoBottom()
|
m.viewport.GotoBottom()
|
||||||
}
|
}
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
|
//Отслеживаем и записываем новые размер окна, вносим изменения в размеры viewport
|
||||||
m.height = msg.Height
|
m.height = msg.Height
|
||||||
m.width = msg.Width
|
m.width = msg.Width
|
||||||
m.initViewport(msg.Width, msg.Height)
|
m.initViewport(msg.Width, msg.Height)
|
||||||
}
|
}
|
||||||
|
//Если сообщение было иным, передаем его напрямую в viewport
|
||||||
|
//todo проверить есть ли эти функции уже у viewport.Update, возможно мой код дублирует
|
||||||
m.viewport, cmd = m.viewport.Update(msg)
|
m.viewport, cmd = m.viewport.Update(msg)
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// View формирует строку для вывода таблицы переменных
|
||||||
func (m *FinalModel) View() string {
|
func (m *FinalModel) View() string {
|
||||||
if !m.ready {
|
//Получаем размеры контейнера и ширину контента
|
||||||
return "Загрузка таблицы"
|
|
||||||
}
|
|
||||||
containerWidth, containerHeight, contentWidth, _ := ui.ContentSizes(m.width, m.height)
|
containerWidth, containerHeight, contentWidth, _ := ui.ContentSizes(m.width, m.height)
|
||||||
|
|
||||||
m.rebuildTable(contentWidth)
|
//Формируем шапку с заголовком
|
||||||
|
|
||||||
header := ui.FinalHeader.
|
header := ui.FinalHeader.
|
||||||
Width(contentWidth).
|
Width(contentWidth).
|
||||||
Render("Итоговые значения переменных")
|
Render("Итоговые значения переменных")
|
||||||
|
//Формируем "подвал"
|
||||||
footer := ui.FinalFooter.
|
footer := ui.FinalFooter.
|
||||||
Width(contentWidth).
|
Width(contentWidth).
|
||||||
Render(fmt.Sprintf("Строк: %d • q — выход • ↑↓ / jk — скролл", len(m.variables)))
|
Render(fmt.Sprintf("Количество переменных: %d", len(m.variables)))
|
||||||
|
|
||||||
renderedTable := m.table.Render()
|
//Обновляем отображение таблицы
|
||||||
content := lipgloss.NewStyle().
|
m.rebuildTable(contentWidth)
|
||||||
Align(lipgloss.Center).
|
//Формируем единый контент из шапки, видимой области и подвала
|
||||||
Render(renderedTable)
|
fullContent := lipgloss.JoinVertical(lipgloss.Top, header, m.viewport.View(), footer)
|
||||||
m.viewport.SetContent(content)
|
|
||||||
body := m.viewport.View()
|
|
||||||
fullContent := lipgloss.JoinVertical(lipgloss.Top, header, body, footer)
|
|
||||||
|
|
||||||
|
//Формируем главный контейнер
|
||||||
container := ui.AppContainer.
|
container := ui.AppContainer.
|
||||||
Width(containerWidth).
|
Width(containerWidth).
|
||||||
Height(containerHeight).
|
Height(containerHeight).
|
||||||
|
|
@ -103,6 +113,7 @@ func (m *FinalModel) View() string {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initViewport создание видимой области таблицы
|
||||||
func (m *FinalModel) initViewport(width, height int) {
|
func (m *FinalModel) initViewport(width, height int) {
|
||||||
|
|
||||||
_, _, contentWidth, contentHeight := ui.ContentSizes(width, height)
|
_, _, contentWidth, contentHeight := ui.ContentSizes(width, height)
|
||||||
|
|
@ -110,15 +121,15 @@ func (m *FinalModel) initViewport(width, height int) {
|
||||||
tableWidth := contentWidth - 2*1
|
tableWidth := contentWidth - 2*1
|
||||||
tableHeight := int(float64(contentHeight)*ui.TableWithRatio) - 2*1
|
tableHeight := int(float64(contentHeight)*ui.TableWithRatio) - 2*1
|
||||||
|
|
||||||
|
//Создание видимой области, куда будет помещена таблица
|
||||||
m.viewport = viewport.New(contentWidth, tableHeight)
|
m.viewport = viewport.New(contentWidth, tableHeight)
|
||||||
//размеры для viewport в нем будет находиться таблица и перемещаться в зависимости от команд
|
|
||||||
|
|
||||||
m.ready = true
|
//Обновление таблицы
|
||||||
m.rebuildTable(tableWidth)
|
m.rebuildTable(tableWidth)
|
||||||
m.viewport.SetContent(m.table.Render())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *FinalModel) rebuildTable(width int) {
|
func (m *FinalModel) rebuildTable(width int) {
|
||||||
|
//Если переменных нет, то делаем спец таблицу
|
||||||
if len(m.variables) == 0 {
|
if len(m.variables) == 0 {
|
||||||
m.table = table.New().
|
m.table = table.New().
|
||||||
Border(lipgloss.RoundedBorder()).
|
Border(lipgloss.RoundedBorder()).
|
||||||
|
|
@ -126,9 +137,11 @@ func (m *FinalModel) rebuildTable(width int) {
|
||||||
StyleFunc(func(row, col int) lipgloss.Style {
|
StyleFunc(func(row, col int) lipgloss.Style {
|
||||||
return ui.FinalTableHeader
|
return ui.FinalTableHeader
|
||||||
}).Headers("Нет переменных")
|
}).Headers("Нет переменных")
|
||||||
|
m.viewport.SetContent(m.table.Render())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
variableNameColWidth := 15
|
//Вычисляем размеры колонок
|
||||||
|
variableNameColWidth := 20
|
||||||
defaultValueColWidth := 25
|
defaultValueColWidth := 25
|
||||||
usedValueColWidth := width - variableNameColWidth - defaultValueColWidth - 4*1
|
usedValueColWidth := width - variableNameColWidth - defaultValueColWidth - 4*1
|
||||||
//ширина таблицы - ширина первой колонки - ширина второй колонки - 4 границы (2 крайние, 2 между колонками)
|
//ширина таблицы - ширина первой колонки - ширина второй колонки - 4 границы (2 крайние, 2 между колонками)
|
||||||
|
|
@ -136,6 +149,7 @@ func (m *FinalModel) rebuildTable(width int) {
|
||||||
usedValueColWidth = 30
|
usedValueColWidth = 30
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Создаем модель таблицы
|
||||||
t := table.New().
|
t := table.New().
|
||||||
Width(variableNameColWidth+defaultValueColWidth+usedValueColWidth+4).
|
Width(variableNameColWidth+defaultValueColWidth+usedValueColWidth+4).
|
||||||
Border(lipgloss.RoundedBorder()).
|
Border(lipgloss.RoundedBorder()).
|
||||||
|
|
@ -162,6 +176,7 @@ func (m *FinalModel) rebuildTable(width int) {
|
||||||
}).
|
}).
|
||||||
Headers("Variable name", "Default value", "Used value")
|
Headers("Variable name", "Default value", "Used value")
|
||||||
|
|
||||||
|
//Заполняем таблицу данными
|
||||||
for _, v := range m.variables {
|
for _, v := range m.variables {
|
||||||
used := fmt.Sprintf("%v", v.Value)
|
used := fmt.Sprintf("%v", v.Value)
|
||||||
if used == "" {
|
if used == "" {
|
||||||
|
|
@ -169,5 +184,9 @@ func (m *FinalModel) rebuildTable(width int) {
|
||||||
}
|
}
|
||||||
t.Row(v.Name, v.Default, used)
|
t.Row(v.Name, v.Default, used)
|
||||||
}
|
}
|
||||||
|
//записываем таблицу в модель
|
||||||
m.table = t
|
m.table = t
|
||||||
|
|
||||||
|
//Помещение в видимую область таблицы
|
||||||
|
m.viewport.SetContent(m.table.Render())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
101
setup/form.go
101
setup/form.go
|
|
@ -10,20 +10,23 @@ import (
|
||||||
"github.com/muesli/reflow/wordwrap"
|
"github.com/muesli/reflow/wordwrap"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FormModel структура описывающая модель отрисовки формы настройки
|
||||||
type FormModel struct {
|
type FormModel struct {
|
||||||
CurrentSection *types.Section
|
CurrentSection *types.Section //Указатель секции с которой надо работать
|
||||||
FieldModels []fields.FieldModel
|
FieldModels []fields.FieldModel //Массив моделей полей
|
||||||
focus int
|
focus int //Фокус для отслеживания с каким объектом надо работать
|
||||||
renderedContent string
|
renderedContent string //Строка хранения информации о приложении
|
||||||
width int
|
width int //Ширина окна
|
||||||
height int
|
height int //Высота окна
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFormModel создает новую модель формы на основе данных настраиваемого приложения
|
||||||
func NewFormModel(application *types.PackageApplication, sectionIdx, focus, width, height int) *FormModel {
|
func NewFormModel(application *types.PackageApplication, sectionIdx, focus, width, height int) *FormModel {
|
||||||
|
//Получаем секцию которую надо отрисовать
|
||||||
currentSection := &application.Sections[sectionIdx]
|
currentSection := &application.Sections[sectionIdx]
|
||||||
|
//Заполняем массив моделей полей с учетом условий
|
||||||
var fieldModels []fields.FieldModel
|
var fieldModels []fields.FieldModel
|
||||||
for _, sectionField := range currentSection.Fields {
|
for _, sectionField := range currentSection.Fields {
|
||||||
if !CheckCondition(sectionField.Condition, application) {
|
if !CheckCondition(sectionField.Condition, application) {
|
||||||
|
|
@ -66,6 +69,7 @@ func NewFormModel(application *types.PackageApplication, sectionIdx, focus, widt
|
||||||
fieldModels = append(fieldModels, fields.NewInputModel(sectionField.Name, fmt.Sprintf("%v", sectionField.Variable.Value)))
|
fieldModels = append(fieldModels, fields.NewInputModel(sectionField.Name, fmt.Sprintf("%v", sectionField.Variable.Value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Устанавливаем фокус (фокус может быть не равен нулю, если новая форма сформирована после Sugar события, который обновляет форму в ТЕКУЩЕМ состоянии)
|
||||||
if len(fieldModels) > focus {
|
if len(fieldModels) > focus {
|
||||||
fieldModels[focus].Focus()
|
fieldModels[focus].Focus()
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +80,7 @@ func NewFormModel(application *types.PackageApplication, sectionIdx, focus, widt
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
}
|
}
|
||||||
|
//Формируем описание приложения если секция установлена About
|
||||||
if currentSection.Type == types.SectionAbout {
|
if currentSection.Type == types.SectionAbout {
|
||||||
m.renderedContent = simpleAboutRender(currentSection.Paragraphs, width)
|
m.renderedContent = simpleAboutRender(currentSection.Paragraphs, width)
|
||||||
m.focus = 0
|
m.focus = 0
|
||||||
|
|
@ -87,10 +92,12 @@ func (m *FormModel) Init() tea.Cmd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update отслеживает действия с формой
|
||||||
func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
|
//Отслеживаем и записываем новые размеры окна, обновляем рендер about
|
||||||
m.width = msg.Width
|
m.width = msg.Width
|
||||||
m.height = msg.Height
|
m.height = msg.Height
|
||||||
if m.CurrentSection.Type == types.SectionAbout {
|
if m.CurrentSection.Type == types.SectionAbout {
|
||||||
|
|
@ -98,13 +105,22 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
|
//Отслеживаем нажатие клавиш формы
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "esc":
|
case "esc":
|
||||||
|
//Обработчик клавиши escape на форме нужен для закрытия полей поддерживающих открытие.
|
||||||
|
//Обходим все доступные в текущей форме поля и закрываем те, что были открыты.
|
||||||
|
//Проверять на фокус не нужно, так как не предполагается, что у нас может быть открыто сразу несколько полей.
|
||||||
|
//Поэтому просто возвращаем команду по возврату сообщение об обновлении полей.
|
||||||
for _, field := range m.FieldModels {
|
for _, field := range m.FieldModels {
|
||||||
|
//проверяем открыто ли поле
|
||||||
if field.CaptureKeys() {
|
if field.CaptureKeys() {
|
||||||
|
//обновляем поля если оно открыто
|
||||||
return m, m.updateFields(msg)
|
return m, m.updateFields(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Если открытых полей не было найдено, значит escape была нажата для закрытия настройки,
|
||||||
|
//возвращаем команду по возврату сообщения об открытии модального окна
|
||||||
return m, func() tea.Msg {
|
return m, func() tea.Msg {
|
||||||
return types.ShowModalMsg{
|
return types.ShowModalMsg{
|
||||||
Type: types.ModalWarning,
|
Type: types.ModalWarning,
|
||||||
|
|
@ -123,23 +139,28 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "enter":
|
case "enter":
|
||||||
logrus.Infof("sections %s, focus %d, fields len %d", m.CurrentSection.Name, m.focus, len(m.FieldModels))
|
//Обработчик клавиши enter на форме нужен для перехода на следующую секцию.
|
||||||
|
//Проверяем где сейчас находится фокус, если его позиция равняется количеству полей,
|
||||||
|
//значит он вышел за пределы формы и находится на кнопке next section, что ставится последней.
|
||||||
|
//После этого мы сбрасываем фокус и возвращаем команду по возврату сообщения о переходе на следующую секцию
|
||||||
if len(m.FieldModels) == m.focus {
|
if len(m.FieldModels) == m.focus {
|
||||||
m.focus = 0
|
m.focus = 0
|
||||||
return m, func() tea.Msg {
|
return m, func() tea.Msg {
|
||||||
return types.NextSectionMsg{}
|
return types.NextSectionMsg{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "tab", "down", "shift+tab", "up":
|
case "tab", "shift+tab", "down", "up":
|
||||||
//Итак, тут надо учесть динамические поля.
|
//Обработчик клавиш tab, shift+tab, down, up на форме нужен для перехода между объектами формы.
|
||||||
//
|
//Получаем точное имя клавиши нажатия, после проверяем где сейчас находится фокус.
|
||||||
|
//Если фокус не равен количеству полей, то значит фокус сейчас у поля формы.
|
||||||
|
//Получаем имя поля (так как это старый фокус, то и поле на котором мы находимся это предыдущее поле, отсюда и название переменной),
|
||||||
|
//Если поле не открыто, то мы можем перейти на следующее поле, увеличив или уменьшив фокус.
|
||||||
|
//Если фокус равен количеству полей, значит он находится на кнопке переключения секций. Увеличиваем или уменьшаем фокус.
|
||||||
key := msg.String()
|
key := msg.String()
|
||||||
if m.focus != len(m.FieldModels) {
|
if m.focus != len(m.FieldModels) {
|
||||||
logrus.Infof("1, focus is %d", m.focus)
|
|
||||||
//todo исправить фокус
|
//todo исправить фокус
|
||||||
field := m.FieldModels[m.focus]
|
prevField := m.FieldModels[m.focus]
|
||||||
logrus.Infof("field %s, capture keys %t", field.GetLabel(), field.CaptureKeys())
|
if !prevField.CaptureKeys() {
|
||||||
if !field.CaptureKeys() {
|
|
||||||
if key == "tab" || key == "down" {
|
if key == "tab" || key == "down" {
|
||||||
m.focus++
|
m.focus++
|
||||||
} else if key == "shift+tab" || key == "up" {
|
} else if key == "shift+tab" || key == "up" {
|
||||||
|
|
@ -147,13 +168,17 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logrus.Infof("2")
|
|
||||||
if key == "tab" || key == "down" {
|
if key == "tab" || key == "down" {
|
||||||
m.focus++
|
m.focus++
|
||||||
} else if key == "shift+tab" || key == "up" {
|
} else if key == "shift+tab" || key == "up" {
|
||||||
m.focus--
|
m.focus--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Проверка правильности фокуса, чтобы не выйти за границы массива полей формы, необходимо проверить новое значение фокуса.
|
||||||
|
//Если фокус стал больше чем количество полей формы, значит он вышел за пределы всей формы и находится уже даже ниже кнопки,
|
||||||
|
//переводим его на первую позицию, то есть на 0.
|
||||||
|
//Если фокус стал меньше 0, значит он вышел за пределы формы и находится выше первого поля формы,
|
||||||
|
//переводим его на последнюю позицию, то есть на кнопку переключения секции, равную положение которой = количество полей на форме.
|
||||||
if m.focus == len(m.FieldModels)+1 {
|
if m.focus == len(m.FieldModels)+1 {
|
||||||
m.focus = 0
|
m.focus = 0
|
||||||
} else if m.focus < 0 {
|
} else if m.focus < 0 {
|
||||||
|
|
@ -161,30 +186,39 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case types.SelectedOptionMsg:
|
case types.SelectedOptionMsg:
|
||||||
|
//Отслеживаем выбор опции в поле типа select
|
||||||
|
//todo тут вообще логика нужна? Мы ведь в select, через m.chosen записываем значение, а после GetValue метод возвращаем m.chosen
|
||||||
logrus.Infof("chose %+v", msg)
|
logrus.Infof("chose %+v", msg)
|
||||||
}
|
}
|
||||||
|
//Если не было возвращено сообщений, значит действие было произведено с полем, а не с самой формой.
|
||||||
|
//Возвращаем результат функции обновления полей.
|
||||||
return m, m.updateFields(msg)
|
return m, m.updateFields(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// View формирует строку для вывода формы и её контента.
|
||||||
func (m *FormModel) View() string {
|
func (m *FormModel) View() string {
|
||||||
|
//Данные о размерах контейнера и ширины контента внутри
|
||||||
containerWidth, containerHeight, contentWidth, _ := ui.ContentSizes(m.width, m.height)
|
containerWidth, containerHeight, contentWidth, _ := ui.ContentSizes(m.width, m.height)
|
||||||
|
|
||||||
formWidth := contentWidth - 2*1
|
formWidth := contentWidth - 2*1
|
||||||
var strokes []string
|
var strokes []string //отображаемые строки
|
||||||
for i, field := range m.FieldModels {
|
for i, field := range m.FieldModels {
|
||||||
|
//Строка с учетом переноса поля.
|
||||||
|
//Имеется ввиду, что если вводимое значение слишком длинное, то оно будет делиться на несколько строк.
|
||||||
fieldStroke := wordwrap.String(field.View(), formWidth)
|
fieldStroke := wordwrap.String(field.View(), formWidth)
|
||||||
|
//Если фокус равен текущему полю, то выделяем его.
|
||||||
if i == m.focus {
|
if i == m.focus {
|
||||||
strokes = append(strokes, ui.FormFieldFocused.Render(fieldStroke))
|
strokes = append(strokes, ui.FormFieldFocused.Render(fieldStroke))
|
||||||
} else {
|
} else {
|
||||||
strokes = append(strokes, ui.FormFieldUnfocused.Render(fieldStroke))
|
strokes = append(strokes, ui.FormFieldUnfocused.Render(fieldStroke))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Если текущая секция это секция About, то выводим её информацию.
|
||||||
|
//Не предполагается, что информация о приложении и поля для настройки будут в одной секции, поэтому тут идет обнуление прошлых строк.
|
||||||
if m.CurrentSection.Type == types.SectionAbout {
|
if m.CurrentSection.Type == types.SectionAbout {
|
||||||
strokes = append([]string{}, m.renderedContent)
|
strokes = append([]string{}, m.renderedContent)
|
||||||
}
|
}
|
||||||
|
//Строка кнопки переключения секций.
|
||||||
|
//Если позиция фокуса равняется количеству полей, значит он сейчас находится на кнопке.
|
||||||
nextBtn := "[ next section ]"
|
nextBtn := "[ next section ]"
|
||||||
if m.focus == len(m.FieldModels) {
|
if m.focus == len(m.FieldModels) {
|
||||||
strokes = append(strokes, ui.FormNextButton.Render(nextBtn))
|
strokes = append(strokes, ui.FormNextButton.Render(nextBtn))
|
||||||
|
|
@ -192,6 +226,8 @@ func (m *FormModel) View() string {
|
||||||
strokes = append(strokes, ui.FormFieldUnfocused.Render(nextBtn))
|
strokes = append(strokes, ui.FormFieldUnfocused.Render(nextBtn))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Основной контейнер приложения.
|
||||||
|
//Устанавливаем ему ширину и высоту, объединяем все строки в одну с соединением, через двойной перевод строки.
|
||||||
container := ui.AppContainer.
|
container := ui.AppContainer.
|
||||||
Width(containerWidth).
|
Width(containerWidth).
|
||||||
Height(containerHeight).
|
Height(containerHeight).
|
||||||
|
|
@ -204,20 +240,28 @@ func (m *FormModel) View() string {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateFields обновляет поля и их внутренние данные, формирует единую команду действий для всех полей, в том числе возврат SugarMsg
|
||||||
func (m *FormModel) updateFields(msg tea.Msg) tea.Cmd {
|
func (m *FormModel) updateFields(msg tea.Msg) tea.Cmd {
|
||||||
|
//Массив команд для выполнения
|
||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
for i, fieldModel := range m.FieldModels {
|
for i, fieldModel := range m.FieldModels {
|
||||||
var fieldCmd tea.Cmd
|
var fieldCmd tea.Cmd
|
||||||
var updated fields.FieldModel
|
var updated fields.FieldModel
|
||||||
if i == m.focus { //если фокус установлен на текущем поле, то текущее поле активируем и передаем ему сообщения для обновления
|
//Если фокус установлен на текущем поле, то нужно активировать его и передать ему сообщение для обновления.
|
||||||
|
//Если фокус не установлен на текущем поле, то снимаем с него фокус и передаем ему сообщение не связанное с нажатием кнопок клавиатуры
|
||||||
|
if i == m.focus {
|
||||||
fieldModel.Focus()
|
fieldModel.Focus()
|
||||||
updated, fieldCmd = fieldModel.Update(msg)
|
updated, fieldCmd = fieldModel.Update(msg)
|
||||||
} else { //если фокус не установлен на текущем поле, то убираем с неё фокус, передаем сообщения, но только те, что не содержат действий клавиатуры
|
} else {
|
||||||
fieldModel.Blur()
|
fieldModel.Blur()
|
||||||
if _, ok := msg.(tea.KeyMsg); !ok {
|
if _, ok := msg.(tea.KeyMsg); !ok {
|
||||||
updated, fieldCmd = fieldModel.Update(msg)
|
updated, fieldCmd = fieldModel.Update(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Если поле успешно вернуло обновленное поле, то необходимо:
|
||||||
|
//обновить модель поля в текущей форме;
|
||||||
|
//проверить изменилось ли значение переменной в настройках приложения,
|
||||||
|
//если изменилось, в массив команд помещаем команду по возврату сообщения Sugar
|
||||||
if updated != nil {
|
if updated != nil {
|
||||||
m.FieldModels[i] = updated
|
m.FieldModels[i] = updated
|
||||||
for _, sectionField := range m.CurrentSection.Fields {
|
for _, sectionField := range m.CurrentSection.Fields {
|
||||||
|
|
@ -234,17 +278,21 @@ func (m *FormModel) updateFields(msg tea.Msg) tea.Cmd {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
sectionField.Variable.Value = newValue //recompute вызывать.
|
sectionField.Variable.Value = newValue //recompute вызывать.
|
||||||
|
//todo что? надо на гите посмотреть в истории, может я это уже сделал, ведь sugar Msg отправляется
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Если есть команда поля, то также помещаем её в массив команд
|
||||||
if fieldCmd != nil {
|
if fieldCmd != nil {
|
||||||
cmds = append(cmds, fieldCmd)
|
cmds = append(cmds, fieldCmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Объединяем весь массив команду в одну
|
||||||
return tea.Batch(cmds...)
|
return tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simpleAboutRender формирует единую строку, описывающую настраиваемое приложение.
|
||||||
func simpleAboutRender(paragraphs []string, width int) string {
|
func simpleAboutRender(paragraphs []string, width int) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
for _, p := range paragraphs {
|
for _, p := range paragraphs {
|
||||||
|
|
@ -256,9 +304,10 @@ func simpleAboutRender(paragraphs []string, width int) string {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(p, "# "):
|
case strings.HasPrefix(p, "# "):
|
||||||
// Заголовок 1 уровня
|
// Заголовок 1 уровня
|
||||||
h1Len := utf8.RuneCountInString(p[2:])
|
//h1Len := utf8.RuneCountInString(p[2:])
|
||||||
h1Offset := strings.Repeat(" ", (width-h1Len)/2)
|
//h1Offset := strings.Repeat(" ", (width-h1Len)/2)
|
||||||
sb.WriteString(ui.AboutTitle.Render(h1Offset+p[2:]) + "\n\n")
|
//sb.WriteString(ui.AboutTitle.Render(h1Offset+p[2:]) + "\n\n")
|
||||||
|
sb.WriteString(ui.AboutTitle.Render(p[2:]) + "\n\n")
|
||||||
case strings.HasPrefix(p, "## "):
|
case strings.HasPrefix(p, "## "):
|
||||||
// Заголовок 2 уровня
|
// Заголовок 2 уровня
|
||||||
sb.WriteString(ui.AboutH2.Render(p[3:]) + "\n\n")
|
sb.WriteString(ui.AboutH2.Render(p[3:]) + "\n\n")
|
||||||
|
|
|
||||||
|
|
@ -10,38 +10,46 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ListApplicationItem структура для описывающая один элемент списка
|
||||||
type ListApplicationItem struct {
|
type ListApplicationItem struct {
|
||||||
Application types.PackageApplication
|
Application types.PackageApplication // Приложения для настройки
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Title метод для получения имени пункта списка
|
||||||
func (i ListApplicationItem) Title() string {
|
func (i ListApplicationItem) Title() string {
|
||||||
return fmt.Sprintf("%s, v%s", i.Application.Name, i.Application.Version)
|
return fmt.Sprintf("%s, v%s", i.Application.Name, i.Application.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Description метод для получения описания пункта списка
|
||||||
func (i ListApplicationItem) Description() string {
|
func (i ListApplicationItem) Description() string {
|
||||||
return fmt.Sprintf("%s", i.Application.Description)
|
return fmt.Sprintf("%s", i.Application.Description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterValue метод для получения строки, по которой будет происходить фильтрация
|
||||||
func (i ListApplicationItem) FilterValue() string {
|
func (i ListApplicationItem) FilterValue() string {
|
||||||
return i.Application.Name
|
return i.Application.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListApplicationsModel структура описывающая модель отрисовки списка приложений
|
||||||
type ListApplicationsModel struct {
|
type ListApplicationsModel struct {
|
||||||
List list.Model
|
List list.Model //Готовый компонент модели списка
|
||||||
width int
|
width int //Ширина окна
|
||||||
height int
|
height int //Высота окна
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewListApplicationsModel создает новую модель списка на основе переданного массива приложений
|
||||||
func NewListApplicationsModel(applications []types.PackageApplication, width, height int) *ListApplicationsModel {
|
func NewListApplicationsModel(applications []types.PackageApplication, width, height int) *ListApplicationsModel {
|
||||||
|
//Заполняем массив приложений
|
||||||
items := make([]list.Item, len(applications))
|
items := make([]list.Item, len(applications))
|
||||||
for i, app := range applications {
|
for i, app := range applications {
|
||||||
items[i] = ListApplicationItem{app}
|
items[i] = ListApplicationItem{app}
|
||||||
}
|
}
|
||||||
|
//delegate нужен для функционала работы с элементом списка
|
||||||
delegate := list.NewDefaultDelegate()
|
delegate := list.NewDefaultDelegate()
|
||||||
delegate.Styles.SelectedTitle = ui.ListSelectedStyle
|
delegate.Styles.SelectedTitle = ui.ListSelectedStyle
|
||||||
delegate.Styles.SelectedDesc = ui.ListSelectedStyle
|
delegate.Styles.SelectedDesc = ui.ListSelectedStyle
|
||||||
delegate.ShowDescription = false
|
delegate.ShowDescription = false
|
||||||
|
//Формируем новый список
|
||||||
l := list.New(items, delegate, width, height)
|
l := list.New(items, delegate, width, height)
|
||||||
l.Title = "Приложения"
|
l.Title = "Приложения"
|
||||||
l.Styles.Title = ui.ListTitleStyle
|
l.Styles.Title = ui.ListTitleStyle
|
||||||
|
|
@ -57,16 +65,22 @@ func (m *ListApplicationsModel) Init() tea.Cmd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update отслеживает действия со списком приложений
|
||||||
func (m *ListApplicationsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *ListApplicationsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
|
//Отслеживаем и записываем новые размеры окна
|
||||||
m.width = msg.Width
|
m.width = msg.Width
|
||||||
m.height = msg.Height
|
m.height = msg.Height
|
||||||
return m, nil
|
return m, nil
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
|
//Отслеживаем нажатия клавиш списка
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "enter":
|
case "enter":
|
||||||
|
//Обработчик enter нужен для подтверждения элемента списка.
|
||||||
|
//Получаем информацию о выбранном приложении,
|
||||||
|
//возвращаем команду по возврату сообщения о показе модального окна подтверждения настройки приложения.
|
||||||
selected := m.List.SelectedItem().(ListApplicationItem)
|
selected := m.List.SelectedItem().(ListApplicationItem)
|
||||||
return m, func() tea.Msg {
|
return m, func() tea.Msg {
|
||||||
return types.ShowModalMsg{
|
return types.ShowModalMsg{
|
||||||
|
|
@ -89,23 +103,27 @@ func (m *ListApplicationsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Если ранее не было возвращено команд, то передаем сообщение напрямую в список
|
||||||
m.List, cmd = m.List.Update(msg)
|
m.List, cmd = m.List.Update(msg)
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// View формирует строку для вывода списка приложений
|
||||||
func (m *ListApplicationsModel) View() string {
|
func (m *ListApplicationsModel) View() string {
|
||||||
|
//Строка отступа между списком и описанием
|
||||||
gapStroke := " "
|
gapStroke := " "
|
||||||
|
//Данные о размерах контейнера и контента внутри
|
||||||
containerWidth, containerHeight, contentWidth, contentHeight := ui.ContentSizes(m.width, m.height)
|
containerWidth, containerHeight, contentWidth, contentHeight := ui.ContentSizes(m.width, m.height)
|
||||||
|
|
||||||
|
//Ширина списка = пропорция от ширины контента - границы для контейнера списка
|
||||||
listWidth := int(float64(contentWidth)*ui.ListWithRatio) - 2*1
|
listWidth := int(float64(contentWidth)*ui.ListWithRatio) - 2*1
|
||||||
//ширина листа = пропорция от ширины контента - границы для контейнера листа
|
//Высота списка = высота контента - границы для контейнера списка
|
||||||
listHeight := contentHeight - 2*1
|
listHeight := contentHeight - 2*1
|
||||||
//высота листа = высота контента - границы для контейнера листа
|
//Ширина описания = обратная пропорция списка от ширины контента - границы для контейнера описания - отступ от контейнера списка
|
||||||
descWidth := int(float64(contentWidth)*(1-ui.ListWithRatio)) - 2*1 - len(gapStroke)
|
descWidth := int(float64(contentWidth)*(1-ui.ListWithRatio)) - 2*1 - len(gapStroke)
|
||||||
//ширина описания = обратная пропорция листа от ширины контента - границы для контейнера описания - отступ от контейнера листа
|
//Высота описания = высота контента - границы для контейнера описания
|
||||||
descHeight := contentHeight - 2*1
|
descHeight := contentHeight - 2*1
|
||||||
//высота листа = высота контента - границы для контейнера описания
|
//Установка размеров списка
|
||||||
|
|
||||||
m.List.SetSize(listWidth, listHeight)
|
m.List.SetSize(listWidth, listHeight)
|
||||||
|
|
||||||
//logs.go logrus.DebugLevel set
|
//logs.go logrus.DebugLevel set
|
||||||
|
|
@ -118,30 +136,26 @@ func (m *ListApplicationsModel) View() string {
|
||||||
descWidth, descHeight,
|
descWidth, descHeight,
|
||||||
)
|
)
|
||||||
|
|
||||||
listView := lipgloss.NewStyle().
|
//Формируем строку списка
|
||||||
|
listView := ui.ListContainerStyle.
|
||||||
Width(listWidth).
|
Width(listWidth).
|
||||||
Height(listHeight).
|
Height(listHeight).
|
||||||
Border(lipgloss.RoundedBorder()).
|
|
||||||
BorderForeground(ui.ColorPurple).
|
|
||||||
AlignVertical(lipgloss.Top).
|
|
||||||
Render(m.List.View())
|
Render(m.List.View())
|
||||||
|
|
||||||
|
//Получаем описание
|
||||||
var desc string
|
var desc string
|
||||||
if sel := m.List.SelectedItem(); sel != nil {
|
if sel := m.List.SelectedItem(); sel != nil {
|
||||||
desc = sel.(ListApplicationItem).Description()
|
desc = sel.(ListApplicationItem).Description()
|
||||||
}
|
}
|
||||||
|
//Формируем строку с описанием
|
||||||
descView := ui.ListDescriptionStyle.
|
descView := ui.ListDescriptionContainerStyle.
|
||||||
Width(descWidth).
|
Width(descWidth).
|
||||||
Height(descHeight).
|
Height(descHeight).
|
||||||
Border(lipgloss.RoundedBorder()).
|
Render(desc)
|
||||||
BorderForeground(ui.ColorError).
|
|
||||||
AlignVertical(lipgloss.Top).
|
|
||||||
Render(
|
|
||||||
desc,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
//Объединяем список и описание в один контент с отступом
|
||||||
body := lipgloss.JoinHorizontal(lipgloss.Center, listView, gapStroke, descView)
|
body := lipgloss.JoinHorizontal(lipgloss.Center, listView, gapStroke, descView)
|
||||||
|
//Формируем главный контейнер
|
||||||
container := ui.AppContainer.
|
container := ui.AppContainer.
|
||||||
Width(containerWidth).
|
Width(containerWidth).
|
||||||
Height(containerHeight).
|
Height(containerHeight).
|
||||||
|
|
|
||||||
104
setup/main.go
104
setup/main.go
|
|
@ -3,9 +3,7 @@ package setup
|
||||||
import (
|
import (
|
||||||
"bubble/bootstrap"
|
"bubble/bootstrap"
|
||||||
"bubble/types"
|
"bubble/types"
|
||||||
"fmt"
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Start() {
|
func Start() {
|
||||||
|
|
@ -15,23 +13,27 @@ func Start() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main структура описывающая объект настроек
|
||||||
type Main struct {
|
type Main struct {
|
||||||
current tea.Model
|
current tea.Model //Текущая модель для отображения
|
||||||
overlay tea.Model
|
overlay tea.Model //Модель для отображения модальных окон
|
||||||
Applications []types.PackageApplication
|
Applications []types.PackageApplication //Список доступных приложений для настройки
|
||||||
CurrentApplication *types.PackageApplication
|
CurrentApplication *types.PackageApplication //Выбранное приложение для настройки
|
||||||
CurrentSection *types.Section
|
CurrentSection *types.Section //Текущая секция
|
||||||
CurrentSectionIdx int
|
CurrentSectionIdx int //Индекс текущей секции
|
||||||
Width int
|
Width int //Ширина окна
|
||||||
Height int
|
Height int //Высота окна
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMain создаем новый объект настройки
|
||||||
func NewMain() *Main {
|
func NewMain() *Main {
|
||||||
|
//Читаем все конфиги приложении
|
||||||
apps := bootstrap.LoadApplications()
|
apps := bootstrap.LoadApplications()
|
||||||
m := &Main{
|
m := &Main{
|
||||||
Applications: apps,
|
Applications: apps,
|
||||||
CurrentSectionIdx: 0,
|
CurrentSectionIdx: 0,
|
||||||
}
|
}
|
||||||
|
//Записываем в текущую модель список приложений
|
||||||
m.current = NewListApplicationsModel(m.Applications, m.Width, m.Height)
|
m.current = NewListApplicationsModel(m.Applications, m.Width, m.Height)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
@ -40,16 +42,20 @@ func (m *Main) Init() tea.Cmd {
|
||||||
return m.current.Init()
|
return m.current.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update отслеживает действия в объекте настроек
|
||||||
func (m *Main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *Main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
|
var cmd tea.Cmd
|
||||||
|
//Если у нас установлен модель модального окна, то передаем сообщение в его функцию обновления,
|
||||||
|
//при возврате команды модального окна записываем её в массив команд.
|
||||||
|
//В противном случае передаем сообщение в функцию обновления текущей модели,
|
||||||
|
//при возврате команды также записываем её в массив команд.
|
||||||
if m.overlay != nil {
|
if m.overlay != nil {
|
||||||
var cmd tea.Cmd
|
|
||||||
m.overlay, cmd = m.overlay.Update(msg)
|
m.overlay, cmd = m.overlay.Update(msg)
|
||||||
if cmd != nil {
|
if cmd != nil {
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var cmd tea.Cmd
|
|
||||||
m.current, cmd = m.current.Update(msg)
|
m.current, cmd = m.current.Update(msg)
|
||||||
if cmd != nil {
|
if cmd != nil {
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
|
@ -57,17 +63,17 @@ func (m *Main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
|
//Отслеживаем и записываем новые размеры окна.
|
||||||
|
//Передавать сообщение в модель текущую или модального окна для вызова их обработки изменения размеров не нужно,
|
||||||
|
//они получили WindowSizeMsg выше, когда мы передаем им переменную msg.
|
||||||
m.Width = msg.Width
|
m.Width = msg.Width
|
||||||
m.Height = msg.Height
|
m.Height = msg.Height
|
||||||
if m.current != nil {
|
|
||||||
m.current, _ = m.current.Update(msg)
|
|
||||||
}
|
|
||||||
if m.overlay != nil {
|
|
||||||
m.overlay, _ = m.overlay.Update(msg)
|
|
||||||
}
|
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
|
//Отслеживаем нажатие клавиш приложения
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "ctrl+c":
|
case "ctrl+c":
|
||||||
|
//Обработчик клавиш ctrl+c у приложения нужен для его закрытия.
|
||||||
|
//Возвращаем команду по возврату сообщения о показе модального окна
|
||||||
return m, func() tea.Msg {
|
return m, func() tea.Msg {
|
||||||
return types.ShowModalMsg{
|
return types.ShowModalMsg{
|
||||||
Type: types.ModalWarning,
|
Type: types.ModalWarning,
|
||||||
|
|
@ -87,8 +93,12 @@ func (m *Main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case types.NextSectionMsg:
|
case types.NextSectionMsg:
|
||||||
|
//Получаем сообщение о переходе на следующую секцию настраиваемого приложения.
|
||||||
|
//Увеличиваем индекс текущей секции.
|
||||||
m.CurrentSectionIdx++
|
m.CurrentSectionIdx++
|
||||||
|
//Если новый индекс равен
|
||||||
if m.CurrentSectionIdx == len(m.CurrentApplication.Sections) {
|
if m.CurrentSectionIdx == len(m.CurrentApplication.Sections) {
|
||||||
|
//todo что делать когда секций больше нужного?
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
for sectionIdx := m.CurrentSectionIdx; sectionIdx < len(m.CurrentApplication.Sections); sectionIdx++ {
|
for sectionIdx := m.CurrentSectionIdx; sectionIdx < len(m.CurrentApplication.Sections); sectionIdx++ {
|
||||||
|
|
@ -134,28 +144,51 @@ func (m *Main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//если секция не была найдена, то выходим
|
//Если секция доступная к выводу не была найдена, то возвращаем модальное окно с выходом к списку приложений
|
||||||
if m.CurrentSection == nil {
|
if m.CurrentSection == nil {
|
||||||
logrus.Infof("select app, eq nil, out")
|
return m, func() tea.Msg {
|
||||||
return m, tea.Quit
|
return types.ShowModalMsg{
|
||||||
|
Type: types.ModalError,
|
||||||
|
Title: "Ошибка",
|
||||||
|
Message: "Ошибка выбора приложения. Указанное приложение не имеет доступных секций." +
|
||||||
|
"Проверьте конфиг настраиваемого приложения на предмет выполнения условий секций",
|
||||||
|
ModalButtons: []types.ModalButton{{Text: "Вернуться к списку приложений", Value: true}},
|
||||||
|
OnResult: func(result any) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
return types.BackToListMsg{}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//создаем новую форму
|
//создаем новую форму
|
||||||
m.current = NewFormModel(m.CurrentApplication, m.CurrentSectionIdx, 0, m.Width, m.Height)
|
m.current = NewFormModel(m.CurrentApplication, m.CurrentSectionIdx, 0, m.Width, m.Height)
|
||||||
case types.BackToListMsg:
|
case types.BackToListMsg:
|
||||||
|
//Получаем сообщение о возврате к списку приложений.
|
||||||
|
//Стираем текущие приложение, стираем текущую секцию, стираем индекс секции, записываем в текущую модель список приложений
|
||||||
m.CurrentApplication = nil
|
m.CurrentApplication = nil
|
||||||
m.CurrentSection = nil
|
m.CurrentSection = nil
|
||||||
m.CurrentSectionIdx = 0
|
m.CurrentSectionIdx = 0
|
||||||
m.current = NewListApplicationsModel(m.Applications, m.Width, m.Height)
|
m.current = NewListApplicationsModel(m.Applications, m.Width, m.Height)
|
||||||
case types.ShowModalMsg:
|
case types.ShowModalMsg:
|
||||||
|
//Получаем сообщение о показе модального окна.
|
||||||
|
//В поле модального окна записываем новый объект модального окна на основе сообщений.
|
||||||
|
//НЕ СТИРАЕМ ТЕКУЩЕЕ ПРИЛОЖЕНИЕ
|
||||||
m.overlay = NewModalModel(msg, m.Width, m.Height)
|
m.overlay = NewModalModel(msg, m.Width, m.Height)
|
||||||
return m, nil
|
return m, nil
|
||||||
case types.CloseModalMsg:
|
case types.CloseModalMsg:
|
||||||
|
//Получаем сообщение о закрытии модального окна.
|
||||||
|
//Стираем модальное окно.
|
||||||
m.overlay = nil
|
m.overlay = nil
|
||||||
return m, tea.Batch(cmds...)
|
return m, nil
|
||||||
|
//предохранитель
|
||||||
|
//return m,tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
//Возвращаем команду собранную из массива команд текущей модели
|
||||||
return m, tea.Batch(cmds...)
|
return m, tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// View формирует строку на основе модального окна или текущей модели
|
||||||
func (m *Main) View() string {
|
func (m *Main) View() string {
|
||||||
view := m.current.View()
|
view := m.current.View()
|
||||||
if m.overlay != nil {
|
if m.overlay != nil {
|
||||||
|
|
@ -164,10 +197,14 @@ func (m *Main) View() string {
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sugar проверяет поле указанное в сообщение на важность в форме и приложении.
|
||||||
func (m *Main) Sugar(sugar types.SugarMsg) {
|
func (m *Main) Sugar(sugar types.SugarMsg) {
|
||||||
|
//Если полей с указанной переменной несколько, то выключаем все прочие поля кроме, указанного в сообщении
|
||||||
if m.IsMultiField(sugar.VariableName) {
|
if m.IsMultiField(sugar.VariableName) {
|
||||||
m.DeactivateField(sugar.VariableName, sugar.FieldName)
|
m.DeactivateField(sugar.VariableName, sugar.FieldName)
|
||||||
}
|
}
|
||||||
|
//Если от переменной поля зависит секции или поля,
|
||||||
|
//то вызываем перерасчет значений compute полей и формируем новую форму с учетом прошлого фокуса на форме
|
||||||
if IsParent(sugar.VariableName, m.CurrentApplication.Sections) {
|
if IsParent(sugar.VariableName, m.CurrentApplication.Sections) {
|
||||||
if sugar.Compute != "" {
|
if sugar.Compute != "" {
|
||||||
Recompute(m.CurrentApplication)
|
Recompute(m.CurrentApplication)
|
||||||
|
|
@ -176,6 +213,7 @@ func (m *Main) Sugar(sugar types.SugarMsg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsParent проверяет являться ли указанная переменная условие для поля или секции
|
||||||
func IsParent(variableName string, sections []types.Section) bool {
|
func IsParent(variableName string, sections []types.Section) bool {
|
||||||
for _, section := range sections {
|
for _, section := range sections {
|
||||||
for _, parent := range section.Parent {
|
for _, parent := range section.Parent {
|
||||||
|
|
@ -195,6 +233,7 @@ func IsParent(variableName string, sections []types.Section) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsMultiField проверяет переменную на использование несколькими полями.
|
||||||
func (m *Main) IsMultiField(variableName string) bool {
|
func (m *Main) IsMultiField(variableName string) bool {
|
||||||
var count int
|
var count int
|
||||||
for _, section := range m.CurrentApplication.Sections {
|
for _, section := range m.CurrentApplication.Sections {
|
||||||
|
|
@ -207,6 +246,7 @@ func (m *Main) IsMultiField(variableName string) bool {
|
||||||
return count > 1
|
return count > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeactivateField отключает все поля использующие указанную переменную, кроме поля с указанным именем
|
||||||
func (m *Main) DeactivateField(variableName, fieldName string) {
|
func (m *Main) DeactivateField(variableName, fieldName string) {
|
||||||
for sectionIdx, section := range m.CurrentApplication.Sections {
|
for sectionIdx, section := range m.CurrentApplication.Sections {
|
||||||
for fieldIdx, field := range section.Fields {
|
for fieldIdx, field := range section.Fields {
|
||||||
|
|
@ -220,23 +260,3 @@ func (m *Main) DeactivateField(variableName, fieldName string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Main) RecomputeFields() {
|
|
||||||
for i := range m.CurrentApplication.Sections {
|
|
||||||
section := &m.CurrentApplication.Sections[i]
|
|
||||||
for j := range section.Fields {
|
|
||||||
field := §ion.Fields[j]
|
|
||||||
if field.FieldType == "compute" && field.Compute != "" {
|
|
||||||
if field.Variable != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
value, err := ComputeValue(field.Compute, m.CurrentApplication)
|
|
||||||
if err != nil {
|
|
||||||
value = fmt.Sprintf("Ошибка вычисления: %v", err)
|
|
||||||
}
|
|
||||||
field.Variable.Value = value
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
105
setup/modal.go
105
setup/modal.go
|
|
@ -7,21 +7,20 @@ import (
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ModalModel структура описывающая модель отрисовки модального окна
|
||||||
type ModalModel struct {
|
type ModalModel struct {
|
||||||
modalType types.ModalType
|
modalType types.ModalType //Тип модального окна
|
||||||
title string
|
title string //Заголовок модального окна
|
||||||
message string
|
message string //Сообщение модального окна
|
||||||
buttons []types.ModalButton
|
buttons []types.ModalButton //Кнопки модального окна
|
||||||
focus int
|
focus int //Фокус для отслеживания с каким объектом надо работать
|
||||||
width int
|
width int //Ширина окна
|
||||||
height int
|
height int //Высота окна
|
||||||
onResult func(result any) tea.Cmd
|
onResult func(result any) tea.Cmd //Какое действие надо выполнить при нажатии
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewModalModel создает новую модель модального окна на основе переданного сообщения
|
||||||
func NewModalModel(msg types.ShowModalMsg, width, height int) *ModalModel {
|
func NewModalModel(msg types.ShowModalMsg, width, height int) *ModalModel {
|
||||||
if len(msg.ModalButtons) == 0 {
|
|
||||||
msg.ModalButtons = []types.ModalButton{{Text: "Ok", Value: true}}
|
|
||||||
}
|
|
||||||
return &ModalModel{
|
return &ModalModel{
|
||||||
modalType: msg.Type,
|
modalType: msg.Type,
|
||||||
title: msg.Title,
|
title: msg.Title,
|
||||||
|
|
@ -38,32 +37,46 @@ func (m *ModalModel) Init() tea.Cmd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update отслеживает действия с модальным окном
|
||||||
func (m *ModalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *ModalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
|
//Отслеживаем и записываем новые размеры окна
|
||||||
m.width = msg.Width
|
m.width = msg.Width
|
||||||
m.height = msg.Height
|
m.height = msg.Height
|
||||||
return m, nil
|
return m, nil
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
|
//Отслеживаем нажатие клавиш модального окна
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "left", "h", "shift+tab":
|
case "left", "shift+tab":
|
||||||
|
//Обработчик нажатия клавиш left и shift+tab нужен для перевода фокуса на прошлую кнопку.
|
||||||
|
//Уменьшаем фокус, если фокус стал меньше 0, значит он вышел за пределы кнопок,
|
||||||
|
//переводим его на последнюю кнопку.
|
||||||
m.focus--
|
m.focus--
|
||||||
if m.focus < 0 {
|
if m.focus < 0 {
|
||||||
m.focus = len(m.buttons) - 1
|
m.focus = len(m.buttons) - 1
|
||||||
}
|
}
|
||||||
case "right", "l", "tab":
|
case "right", "tab":
|
||||||
|
//Обработчик нажатия клавиш right и tab нужен для перевода фокуса на следующую кнопку.
|
||||||
|
//Увеличиваем фокус, если фокус стал равен количество кнопок, значит он вышел за их пределы,
|
||||||
|
//переводим его на первую позицию, то есть на 0
|
||||||
m.focus++
|
m.focus++
|
||||||
if m.focus >= len(m.buttons) {
|
if m.focus >= len(m.buttons) {
|
||||||
m.focus = 0
|
m.focus = 0
|
||||||
}
|
}
|
||||||
case "enter", " ":
|
case "enter", " ":
|
||||||
//вытаскиваем значение кнопки
|
//Обработчик клавиши enter нужен для подтверждения кнопки.
|
||||||
|
//Получаем значение кнопки, а после если у модального окна прописана функция,
|
||||||
|
//то передаем значение кнопки в функцию модального окна.
|
||||||
result := m.buttons[m.focus].Value
|
result := m.buttons[m.focus].Value
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
if m.onResult != nil { //если функция кнопки не пустая, мы передаем в функцию значение кнопки
|
if m.onResult != nil {
|
||||||
cmd = m.onResult(result)
|
cmd = m.onResult(result)
|
||||||
}
|
}
|
||||||
return m, tea.Batch( //объединяем 2 действия, функцию кнопки и закрытие модального окна
|
//Возвращаем единую команду составленную из двух,
|
||||||
|
//результата выполнения функции модального окна,
|
||||||
|
//функцию возвращения сообщения о закрытии модального окна.
|
||||||
|
return m, tea.Batch(
|
||||||
cmd,
|
cmd,
|
||||||
func() tea.Msg { return types.CloseModalMsg{} },
|
func() tea.Msg { return types.CloseModalMsg{} },
|
||||||
)
|
)
|
||||||
|
|
@ -72,13 +85,14 @@ func (m *ModalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// View формирует строку для вывода модального окна и её контента
|
||||||
func (m *ModalModel) View() string {
|
func (m *ModalModel) View() string {
|
||||||
|
//Данные о размерах контейнера
|
||||||
containerWidth, containerHeight, _, _ := ui.ContentSizes(m.width, m.height)
|
containerWidth, containerHeight, _, _ := ui.ContentSizes(m.width, m.height)
|
||||||
|
//Модальное окно не должно занимать все пространство, поэтому получаем минимально допустимые размеры
|
||||||
modalWidth := min(containerWidth, ui.ModalMaxWidth)
|
modalWidth := min(containerWidth, ui.ModalMaxWidth)
|
||||||
modalHeight := min(containerHeight, ui.ModalMaxHeight)
|
modalHeight := min(containerHeight, ui.ModalMaxHeight)
|
||||||
|
//Формируем просто сообщение и кнопки с учетом фокуса
|
||||||
message := lipgloss.NewStyle().Render(m.message)
|
message := lipgloss.NewStyle().Render(m.message)
|
||||||
var buttons []string
|
var buttons []string
|
||||||
for i, button := range m.buttons {
|
for i, button := range m.buttons {
|
||||||
|
|
@ -88,12 +102,14 @@ func (m *ModalModel) View() string {
|
||||||
}
|
}
|
||||||
buttons = append(buttons, style.Render(button.Text))
|
buttons = append(buttons, style.Render(button.Text))
|
||||||
}
|
}
|
||||||
|
//Строка кнопок
|
||||||
buttonRow := lipgloss.JoinHorizontal(lipgloss.Center, buttons...)
|
buttonRow := lipgloss.JoinHorizontal(lipgloss.Center, buttons...)
|
||||||
|
//Тело контента модального окна
|
||||||
modalBody := lipgloss.JoinVertical(lipgloss.Center, message, "", buttonRow)
|
modalBody := lipgloss.JoinVertical(lipgloss.Center, message, "", buttonRow)
|
||||||
|
|
||||||
|
//В зависимости от типа модального окна разные цвета
|
||||||
var borderColor lipgloss.Color
|
var borderColor lipgloss.Color
|
||||||
var modalTitleStyle lipgloss.Style
|
var modalTitleStyle lipgloss.Style
|
||||||
|
|
||||||
switch m.modalType {
|
switch m.modalType {
|
||||||
case types.ModalError:
|
case types.ModalError:
|
||||||
borderColor = ui.ColorError
|
borderColor = ui.ColorError
|
||||||
|
|
@ -105,33 +121,19 @@ func (m *ModalModel) View() string {
|
||||||
borderColor = ui.ColorAccent
|
borderColor = ui.ColorAccent
|
||||||
modalTitleStyle = ui.ModalTitleDefault
|
modalTitleStyle = ui.ModalTitleDefault
|
||||||
}
|
}
|
||||||
//
|
//Формируем модальный контейнер с контентом внутри и указанным цветом границ.
|
||||||
//switch m.modalType {
|
modalView := ui.ModalContainer.
|
||||||
//case types.ModalError:
|
Width(modalWidth).
|
||||||
// modalStyle = ui.ModalBorderError
|
Height(modalHeight).
|
||||||
// modalTitleStyle = ui.ModalTitleError
|
Align(lipgloss.Center).
|
||||||
//case types.ModalWarning, types.ModalConfirm:
|
Border(lipgloss.DoubleBorder()).
|
||||||
// modalStyle = ui.ModalBorderWarning
|
BorderForeground(borderColor).
|
||||||
// modalTitleStyle = ui.ModalTitleWarning
|
Render(modalBody)
|
||||||
//default:
|
|
||||||
// modalStyle = ui.ModalBorderInfo
|
|
||||||
// modalTitleStyle = ui.ModalTitleDefault
|
|
||||||
//}
|
|
||||||
var modalView string
|
|
||||||
symbolToRepeat := "*"
|
symbolToRepeat := "*"
|
||||||
|
//Если у модального окна есть заголовок, то в modalView записываем новое значение,
|
||||||
|
//ставя заголовок сверху модального контейнера
|
||||||
if m.title != "" {
|
if m.title != "" {
|
||||||
modalView = ui.ModalContainer.
|
|
||||||
Width(modalWidth).
|
|
||||||
Height(modalHeight).
|
|
||||||
Align(lipgloss.Center, lipgloss.Center).
|
|
||||||
Border(lipgloss.DoubleBorder()).
|
|
||||||
BorderForeground(borderColor).
|
|
||||||
Render(modalBody)
|
|
||||||
//modalView = ui.ModalContainer.
|
|
||||||
// Width(modalWidth).
|
|
||||||
// Height(modalHeight).
|
|
||||||
// BorderForeground(borderColor).
|
|
||||||
// Render(modalBody)
|
|
||||||
//repeatCount := modalWidth - utf8.RuneCountInString(m.title)
|
//repeatCount := modalWidth - utf8.RuneCountInString(m.title)
|
||||||
//if repeatCount < 0 {
|
//if repeatCount < 0 {
|
||||||
// repeatCount = 0
|
// repeatCount = 0
|
||||||
|
|
@ -139,19 +141,6 @@ func (m *ModalModel) View() string {
|
||||||
//symbolFill := strings.Repeat(symbolToRepeat, repeatCount/2)
|
//symbolFill := strings.Repeat(symbolToRepeat, repeatCount/2)
|
||||||
//modalView = lipgloss.JoinVertical(lipgloss.Center, modalTitleStyle.Render(symbolFill+m.title+symbolFill), modalView)
|
//modalView = lipgloss.JoinVertical(lipgloss.Center, modalTitleStyle.Render(symbolFill+m.title+symbolFill), modalView)
|
||||||
modalView = lipgloss.JoinVertical(lipgloss.Center, modalTitleStyle.Render(m.title), modalView)
|
modalView = lipgloss.JoinVertical(lipgloss.Center, modalTitleStyle.Render(m.title), modalView)
|
||||||
} else {
|
|
||||||
modalView = ui.ModalContainer.
|
|
||||||
Width(modalWidth).
|
|
||||||
Height(modalHeight).
|
|
||||||
BorderStyle(lipgloss.ASCIIBorder()).
|
|
||||||
BorderForeground(borderColor).
|
|
||||||
Render(modalBody)
|
|
||||||
//modalView = modalStyle.
|
|
||||||
// Width(modalWidth).
|
|
||||||
// Height(modalHeight).
|
|
||||||
// Padding(1, 3).
|
|
||||||
// Border(lipgloss.DoubleBorder()).
|
|
||||||
// Render(modalBody)
|
|
||||||
}
|
}
|
||||||
return lipgloss.Place(m.width, m.height,
|
return lipgloss.Place(m.width, m.height,
|
||||||
lipgloss.Center, lipgloss.Center,
|
lipgloss.Center, lipgloss.Center,
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,14 @@ func ContentSizes(screenWidth, screenHeight int) (containerWidth, containerHeigh
|
||||||
|
|
||||||
// Используемые цвета
|
// Используемые цвета
|
||||||
var (
|
var (
|
||||||
|
// ColorForeground = lipgloss.Color("#c0caf5")
|
||||||
|
// ColorBackground = lipgloss.Color("#1a1b26")
|
||||||
|
// ColorAccent = lipgloss.Color("#7aa2f7")
|
||||||
|
// ColorSuccess = lipgloss.Color("#9ece6a")
|
||||||
|
// ColorError = lipgloss.Color("#f7768e")
|
||||||
|
// ColorWarning = lipgloss.Color("#e0af68")
|
||||||
|
// ColorInfo = lipgloss.Color("#7dcfff")
|
||||||
|
|
||||||
ColorFg = lipgloss.Color("#eeeeee")
|
ColorFg = lipgloss.Color("#eeeeee")
|
||||||
ColorBg = lipgloss.Color("#0d1117")
|
ColorBg = lipgloss.Color("#0d1117")
|
||||||
ColorAccent = lipgloss.Color("#a6e3a1")
|
ColorAccent = lipgloss.Color("#a6e3a1")
|
||||||
|
|
@ -77,6 +85,17 @@ var AppContainer = lipgloss.NewStyle().
|
||||||
|
|
||||||
// Стили для списка приложений
|
// Стили для списка приложений
|
||||||
var (
|
var (
|
||||||
|
ListContainerStyle = lipgloss.NewStyle().
|
||||||
|
BorderStyle(lipgloss.RoundedBorder()).
|
||||||
|
BorderForeground(ColorPurple).
|
||||||
|
AlignVertical(lipgloss.Top)
|
||||||
|
|
||||||
|
ListDescriptionContainerStyle = lipgloss.NewStyle().
|
||||||
|
BorderStyle(lipgloss.RoundedBorder()).
|
||||||
|
BorderForeground(ColorError).
|
||||||
|
Foreground(ColorAccent2).
|
||||||
|
AlignVertical(lipgloss.Top)
|
||||||
|
|
||||||
ListTitleStyle = lipgloss.NewStyle().Foreground(ColorPurple).Italic(true).Bold(true)
|
ListTitleStyle = lipgloss.NewStyle().Foreground(ColorPurple).Italic(true).Bold(true)
|
||||||
ListSelectedStyle = lipgloss.NewStyle().Foreground(ColorError).Bold(true)
|
ListSelectedStyle = lipgloss.NewStyle().Foreground(ColorError).Bold(true)
|
||||||
ListDescriptionStyle = lipgloss.NewStyle().Foreground(ColorAccent2)
|
ListDescriptionStyle = lipgloss.NewStyle().Foreground(ColorAccent2)
|
||||||
|
|
@ -124,12 +143,11 @@ var (
|
||||||
Bold(true).
|
Bold(true).
|
||||||
Align(lipgloss.Center).
|
Align(lipgloss.Center).
|
||||||
MarginBottom(1).
|
MarginBottom(1).
|
||||||
Padding(1, 2)
|
Padding(1, 0)
|
||||||
|
|
||||||
FinalFooter = lipgloss.NewStyle().
|
FinalFooter = lipgloss.NewStyle().
|
||||||
Foreground(ColorGrey).
|
Foreground(ColorGrey).
|
||||||
MarginTop(1).
|
MarginTop(1).
|
||||||
Padding(1, 0).
|
|
||||||
Align(lipgloss.Center)
|
Align(lipgloss.Center)
|
||||||
|
|
||||||
FinalTableBorder = lipgloss.NewStyle().
|
FinalTableBorder = lipgloss.NewStyle().
|
||||||
|
|
@ -154,12 +172,6 @@ var (
|
||||||
ModalContainer = lipgloss.NewStyle().
|
ModalContainer = lipgloss.NewStyle().
|
||||||
Border(lipgloss.DoubleBorder()).
|
Border(lipgloss.DoubleBorder()).
|
||||||
Align(lipgloss.Center, lipgloss.Center)
|
Align(lipgloss.Center, lipgloss.Center)
|
||||||
//Padding(ApplicationVerticalOffset, ApplicationHorizontalOffset)
|
|
||||||
|
|
||||||
ModalBorderError = lipgloss.NewStyle().BorderForeground(ColorError)
|
|
||||||
ModalBorderWarning = lipgloss.NewStyle().BorderForeground(ColorWarning)
|
|
||||||
ModalBorderConfirm = lipgloss.NewStyle().BorderForeground(ColorPurple)
|
|
||||||
ModalBorderInfo = lipgloss.NewStyle().BorderForeground(ColorAccent)
|
|
||||||
|
|
||||||
ModalTitleError = lipgloss.NewStyle().BorderForeground(ColorError).Bold(true)
|
ModalTitleError = lipgloss.NewStyle().BorderForeground(ColorError).Bold(true)
|
||||||
ModalTitleWarning = lipgloss.NewStyle().BorderForeground(ColorWarning).Bold(true)
|
ModalTitleWarning = lipgloss.NewStyle().BorderForeground(ColorWarning).Bold(true)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue