voidcli 0.1.0__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {voidcli-0.1.0 → voidcli-1.1.0}/.gitignore +1 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/PKG-INFO +6 -1
- {voidcli-0.1.0 → voidcli-1.1.0}/README.md +5 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/pyproject.toml +1 -1
- {voidcli-0.1.0 → voidcli-1.1.0}/void/main.py +34 -2
- {voidcli-0.1.0 → voidcli-1.1.0}/void/views/activity.py +11 -7
- {voidcli-0.1.0 → voidcli-1.1.0}/void/views/collection.py +22 -8
- {voidcli-0.1.0 → voidcli-1.1.0}/void/views/note_form.py +12 -11
- voidcli-1.1.0/void/views/note_modal.py +73 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/LICENSE +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/__init__.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/controllers/__init__.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/controllers/activity.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/controllers/category.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/controllers/note.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/database/__init__.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/database/connection.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/database/init_tables.sql +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/models/__init__.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/models/activity.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/models/category.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/models/note.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/views/__init__.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/views/day_note.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/views/note.py +0 -0
- {voidcli-0.1.0 → voidcli-1.1.0}/void/views/welcome.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: voidcli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Project-URL: Repository, https://github.com/NelsonUrrutia/void
|
|
5
5
|
Author-email: Nelson Urrutia <nelurrutia15@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -44,6 +44,7 @@ Description-Content-Type: text/markdown
|
|
|
44
44
|
# **V**ital **O**ffline **I**nformation **D**iary
|
|
45
45
|
|
|
46
46
|
---
|
|
47
|
+

|
|
47
48
|
|
|
48
49
|
> **VOID** is an offline diary to be used in the terminal.
|
|
49
50
|
|
|
@@ -75,3 +76,7 @@ I made it public so there's proof on the Internet of my work.
|
|
|
75
76
|
### How to install
|
|
76
77
|
|
|
77
78
|
The project is available on PyPI, so you can install it with `pipx install voidcli`.
|
|
79
|
+
|
|
80
|
+
### How to use
|
|
81
|
+
|
|
82
|
+
After installation, run `void` in your terminal.
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# **V**ital **O**ffline **I**nformation **D**iary
|
|
13
13
|
|
|
14
14
|
---
|
|
15
|
+

|
|
15
16
|
|
|
16
17
|
> **VOID** is an offline diary to be used in the terminal.
|
|
17
18
|
|
|
@@ -43,3 +44,7 @@ I made it public so there's proof on the Internet of my work.
|
|
|
43
44
|
### How to install
|
|
44
45
|
|
|
45
46
|
The project is available on PyPI, so you can install it with `pipx install voidcli`.
|
|
47
|
+
|
|
48
|
+
### How to use
|
|
49
|
+
|
|
50
|
+
After installation, run `void` in your terminal.
|
|
@@ -45,11 +45,43 @@ class VoidApp(App):
|
|
|
45
45
|
padding: 1 0;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/* FORM FIELDS */
|
|
49
|
+
.field, Select.field > SelectCurrent{
|
|
50
|
+
border: round $panel-lighten-2;
|
|
51
|
+
border-title-color: $text-muted;
|
|
52
|
+
background: transparent;
|
|
53
|
+
padding: 1 2;
|
|
54
|
+
}
|
|
55
|
+
Input.field, Select.field > SelectCurrent{
|
|
56
|
+
height: 5;
|
|
57
|
+
}
|
|
58
|
+
.field:focus, Select.field:focus > SelectCurrent{
|
|
59
|
+
border: round $primary;
|
|
60
|
+
border-title-color: $primary;
|
|
61
|
+
border-title-style: bold;
|
|
62
|
+
}
|
|
63
|
+
Select.field, Select.field:focus{
|
|
64
|
+
border: none;
|
|
65
|
+
padding: 0;
|
|
66
|
+
}
|
|
67
|
+
TextArea.field > .text-area--cursor-line{
|
|
68
|
+
background: transparent;
|
|
69
|
+
}
|
|
70
|
+
Select.field > SelectOverlay{
|
|
71
|
+
border: round $primary;
|
|
72
|
+
background: $background;
|
|
73
|
+
}
|
|
74
|
+
Checkbox.field.-on{
|
|
75
|
+
border: round $success;
|
|
76
|
+
}
|
|
77
|
+
|
|
48
78
|
/* CARDS */
|
|
49
79
|
.note_card{
|
|
50
80
|
height: auto;
|
|
51
|
-
padding: 0 2;
|
|
52
|
-
border: round $primary
|
|
81
|
+
padding: 1 2 0 2;
|
|
82
|
+
border: round $primary 40%;
|
|
83
|
+
border-title-color: $accent;
|
|
84
|
+
border-title-style: bold;
|
|
53
85
|
}
|
|
54
86
|
.note_card Label{
|
|
55
87
|
width: 100%;
|
|
@@ -5,6 +5,7 @@ from textual.app import ComposeResult
|
|
|
5
5
|
from textual.containers import Horizontal, Vertical
|
|
6
6
|
from textual.message import Message
|
|
7
7
|
from textual.widgets import Button, DataTable, Input, Label, Select, Static
|
|
8
|
+
from textual.widgets._select import SelectCurrent
|
|
8
9
|
|
|
9
10
|
from void.controllers.activity import ActivityController
|
|
10
11
|
from void.controllers.category import CategoryController
|
|
@@ -29,7 +30,7 @@ class ActivitiesView(Static):
|
|
|
29
30
|
width: 70%;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
#new_category, #new_activity, #category_select{
|
|
33
34
|
margin: 1 0 0 0;
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -75,17 +76,14 @@ class ActivitiesView(Static):
|
|
|
75
76
|
with Horizontal(classes="main_container"):
|
|
76
77
|
with Vertical(id="category_module"):
|
|
77
78
|
yield Label("CATEGORIES", classes="module_title")
|
|
78
|
-
yield
|
|
79
|
-
yield Input(placeholder="Add a new category", id="new_category")
|
|
79
|
+
yield Input(placeholder="Add a new category", id="new_category", classes="field")
|
|
80
80
|
yield Button(label="ADD", flat=True, id="add_category")
|
|
81
81
|
yield DataTable(id="categories_table")
|
|
82
82
|
with Vertical(id="activity_module"):
|
|
83
83
|
with Vertical(id="activity_form"):
|
|
84
84
|
yield Label("ACTIVITIES", classes="module_title")
|
|
85
|
-
yield
|
|
86
|
-
yield
|
|
87
|
-
yield Label("Select a category", classes="field_label")
|
|
88
|
-
yield Select([], type_to_search=True, id="category_select")
|
|
85
|
+
yield Input(placeholder="Add a new activity", id="new_activity", classes="field")
|
|
86
|
+
yield Select([], type_to_search=True, id="category_select", classes="field")
|
|
89
87
|
yield Input(id="activity_id", compact=True)
|
|
90
88
|
with Horizontal(id="activity_form_cta_container"):
|
|
91
89
|
yield Button(label="ADD", flat=True, id="add_activity", variant="success")
|
|
@@ -110,6 +108,12 @@ class ActivitiesView(Static):
|
|
|
110
108
|
self.clear_activity_form_btn = self.query_one("#clear_activity_form", Button)
|
|
111
109
|
self.activities_table = self.query_one("#activities_table", DataTable)
|
|
112
110
|
|
|
111
|
+
# Border titles
|
|
112
|
+
self.add_category_input.border_title = "New Category"
|
|
113
|
+
self.add_activity_input.border_title = "Activity"
|
|
114
|
+
# Select draws its border on its inner SelectCurrent widget
|
|
115
|
+
self.category_selector.query_one(SelectCurrent).border_title = "Select a category"
|
|
116
|
+
|
|
113
117
|
self.init_activities_table()
|
|
114
118
|
self.init_categories_selector()
|
|
115
119
|
self.init_categories_table()
|
|
@@ -2,11 +2,13 @@ from datetime import date
|
|
|
2
2
|
from itertools import groupby
|
|
3
3
|
from typing import override
|
|
4
4
|
|
|
5
|
+
from textual import on
|
|
5
6
|
from textual.app import ComposeResult
|
|
6
|
-
from textual.containers import Grid, Vertical, VerticalScroll
|
|
7
|
-
from textual.widgets import Label, Static
|
|
7
|
+
from textual.containers import Grid, Right, Vertical, VerticalScroll
|
|
8
|
+
from textual.widgets import Button, Label, Static
|
|
8
9
|
|
|
9
10
|
from void.controllers.note import NoteController
|
|
11
|
+
from void.views.note_modal import NoteModal
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class CollectionView(Static):
|
|
@@ -16,9 +18,9 @@ class CollectionView(Static):
|
|
|
16
18
|
grid-size: 3;
|
|
17
19
|
grid-gutter: 1;
|
|
18
20
|
}
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
.note_card_actions{
|
|
22
|
+
height: auto;
|
|
23
|
+
margin: 0 0 1 0;
|
|
22
24
|
}
|
|
23
25
|
"""
|
|
24
26
|
|
|
@@ -26,10 +28,14 @@ class CollectionView(Static):
|
|
|
26
28
|
def __init__(self) -> None:
|
|
27
29
|
super().__init__()
|
|
28
30
|
self.ctrl = NoteController()
|
|
31
|
+
self.notes_by_date: dict[str, list] = {}
|
|
29
32
|
|
|
30
33
|
@override
|
|
31
34
|
def compose(self) -> ComposeResult:
|
|
32
35
|
notes = self.ctrl.get_notes()
|
|
36
|
+
self.notes_by_date = {
|
|
37
|
+
note_date: list(rows) for note_date, rows in groupby(notes, key=lambda row: row["note_date"])
|
|
38
|
+
}
|
|
33
39
|
with Vertical(classes="header"):
|
|
34
40
|
yield Label("VOID COLLECTION", classes="module_title")
|
|
35
41
|
with VerticalScroll(classes="main_container"):
|
|
@@ -37,9 +43,9 @@ class CollectionView(Static):
|
|
|
37
43
|
yield Label("No VOID NOTE saved yet.")
|
|
38
44
|
return
|
|
39
45
|
with Grid(classes="card_grid note_grid"):
|
|
40
|
-
for note_date, rows in
|
|
41
|
-
with Vertical(classes="note_card"):
|
|
42
|
-
|
|
46
|
+
for note_date, rows in self.notes_by_date.items():
|
|
47
|
+
with Vertical(classes="note_card") as card:
|
|
48
|
+
card.border_title = self.format_date(note_date)
|
|
43
49
|
for row in rows:
|
|
44
50
|
with Vertical(classes="entry_card"):
|
|
45
51
|
yield Label(row["activity"], classes="entry_title")
|
|
@@ -47,6 +53,14 @@ class CollectionView(Static):
|
|
|
47
53
|
yield Label(row["notes"], classes="entry_note")
|
|
48
54
|
else:
|
|
49
55
|
yield Label("No notes written.", classes="entry_empty")
|
|
56
|
+
with Right(classes="note_card_actions"):
|
|
57
|
+
yield Button("EXPAND", name=note_date, classes="open_note", flat=True)
|
|
50
58
|
|
|
51
59
|
def format_date(self, date_str):
|
|
52
60
|
return date.fromisoformat(date_str).strftime("%B %d, %Y").upper()
|
|
61
|
+
|
|
62
|
+
@on(Button.Pressed, ".open_note")
|
|
63
|
+
def on_open_note(self, event: Button.Pressed) -> None:
|
|
64
|
+
note_date = event.button.name
|
|
65
|
+
if note_date in self.notes_by_date:
|
|
66
|
+
self.app.push_screen(NoteModal(self.format_date(note_date), self.notes_by_date[note_date]))
|
|
@@ -34,12 +34,11 @@ class NoteForm(Static):
|
|
|
34
34
|
|
|
35
35
|
.category_block{
|
|
36
36
|
height: auto;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
padding: 0 0 1 0;
|
|
37
|
+
margin: 0 0 1 0;
|
|
38
|
+
padding: 1 1 0 1;
|
|
39
|
+
border: round $primary 40%;
|
|
40
|
+
border-title-color: $accent;
|
|
41
|
+
border-title-style: bold;
|
|
43
42
|
}
|
|
44
43
|
.activity_grid{
|
|
45
44
|
grid-size: 2;
|
|
@@ -53,7 +52,7 @@ class NoteForm(Static):
|
|
|
53
52
|
width: 100%;
|
|
54
53
|
}
|
|
55
54
|
.activity_card TextArea{
|
|
56
|
-
height:
|
|
55
|
+
height: 8;
|
|
57
56
|
}
|
|
58
57
|
.activity_handle{
|
|
59
58
|
display: none;
|
|
@@ -90,15 +89,17 @@ class NoteForm(Static):
|
|
|
90
89
|
with VerticalScroll(classes="main_container"):
|
|
91
90
|
for _, category, activities in self.activities_by_category_data:
|
|
92
91
|
if activities:
|
|
93
|
-
with Vertical(classes="category_block"):
|
|
94
|
-
|
|
92
|
+
with Vertical(classes="category_block") as block:
|
|
93
|
+
block.border_title = category
|
|
95
94
|
with Grid(classes="card_grid activity_grid"):
|
|
96
95
|
for id, name in activities:
|
|
97
96
|
handle = f"{id}::{self.handleize(name)}"
|
|
98
97
|
with Vertical(classes="activity_card"):
|
|
99
|
-
yield Checkbox(label=name)
|
|
98
|
+
yield Checkbox(label=name, classes="field")
|
|
100
99
|
yield Input(value=handle, classes="activity_handle")
|
|
101
|
-
|
|
100
|
+
notes = TextArea(classes="field")
|
|
101
|
+
notes.border_title = "Notes"
|
|
102
|
+
yield notes
|
|
102
103
|
|
|
103
104
|
@on(Checkbox.Changed)
|
|
104
105
|
def on_activity_toggled(self) -> None:
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from itertools import groupby
|
|
2
|
+
from typing import override
|
|
3
|
+
|
|
4
|
+
from textual import on
|
|
5
|
+
from textual.app import ComposeResult
|
|
6
|
+
from textual.binding import Binding
|
|
7
|
+
from textual.containers import Center, Vertical, VerticalScroll
|
|
8
|
+
from textual.screen import ModalScreen
|
|
9
|
+
from textual.widgets import Button, Label
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NoteModal(ModalScreen[None]):
|
|
13
|
+
|
|
14
|
+
BINDINGS = [Binding("escape", "close", "Close")]
|
|
15
|
+
|
|
16
|
+
DEFAULT_CSS = """
|
|
17
|
+
NoteModal{
|
|
18
|
+
align: center middle;
|
|
19
|
+
}
|
|
20
|
+
.modal_card{
|
|
21
|
+
width: 90%;
|
|
22
|
+
height: 90%;
|
|
23
|
+
padding: 1 4;
|
|
24
|
+
border: round $primary;
|
|
25
|
+
border-title-color: $accent;
|
|
26
|
+
border-title-style: bold;
|
|
27
|
+
background: $background;
|
|
28
|
+
}
|
|
29
|
+
.modal_card .section_title{
|
|
30
|
+
padding: 1 0 1 0;
|
|
31
|
+
}
|
|
32
|
+
.modal_card .entry_card{
|
|
33
|
+
padding: 1 0 1 2;
|
|
34
|
+
}
|
|
35
|
+
.modal_body{
|
|
36
|
+
height: 1fr;
|
|
37
|
+
}
|
|
38
|
+
.modal_footer{
|
|
39
|
+
height: auto;
|
|
40
|
+
padding: 1 0 0 0;
|
|
41
|
+
}
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
def __init__(self, title: str, rows: list) -> None:
|
|
46
|
+
super().__init__()
|
|
47
|
+
self.note_title = title
|
|
48
|
+
self.rows = sorted(rows, key=lambda row: (row["category"], row["activity"]))
|
|
49
|
+
|
|
50
|
+
@override
|
|
51
|
+
def compose(self) -> ComposeResult:
|
|
52
|
+
with Vertical(classes="modal_card") as card:
|
|
53
|
+
card.border_title = self.note_title
|
|
54
|
+
with VerticalScroll(classes="modal_body"):
|
|
55
|
+
for category, group in groupby(self.rows, key=lambda row: row["category"]):
|
|
56
|
+
entries = list(group)
|
|
57
|
+
yield Label(f"── {category.upper()} · {len(entries)} ", classes="section_title")
|
|
58
|
+
for row in entries:
|
|
59
|
+
with Vertical(classes="entry_card"):
|
|
60
|
+
yield Label(row["activity"], classes="entry_title")
|
|
61
|
+
if row["notes"]:
|
|
62
|
+
yield Label(row["notes"], classes="entry_note")
|
|
63
|
+
else:
|
|
64
|
+
yield Label("No notes written.", classes="entry_empty")
|
|
65
|
+
with Center(classes="modal_footer"):
|
|
66
|
+
yield Button("CLOSE", id="close_note_modal", variant="primary", flat=True)
|
|
67
|
+
|
|
68
|
+
@on(Button.Pressed, "#close_note_modal")
|
|
69
|
+
def on_close_pressed(self) -> None:
|
|
70
|
+
self.action_close()
|
|
71
|
+
|
|
72
|
+
def action_close(self) -> None:
|
|
73
|
+
self.dismiss()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|