Added Todo
This commit is contained in:
1 parent
e735e722f6
commit
feec2917f6
12 files changed
+1160
-1
No files matched your search
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="checkbox-check.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.59658691"
|
||||
inkscape:cx="273.22088"
|
||||
inkscape:cy="432.46004"
|
||||
inkscape:window-width="954"
|
||||
inkscape:window-height="1045"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Interface / Checkbox_Check" />
|
||||
<g
|
||||
id="Interface / Checkbox_Check">
|
||||
<path
|
||||
id="Vector"
|
||||
d="m 8,12 3,3 5,-6 M 4,16.8002 v -9.6 C 4,6.08009 4,5.51962 4.21799,5.0918 4.40973,4.71547 4.71547,4.40973 5.0918,4.21799 5.51962,4 6.08009,4 7.2002,4 h 9.6 c 1.1201,0 1.6794,0 2.1072,0.21799 0.3763,0.19174 0.6831,0.49748 0.8748,0.87381 C 20,5.5192 20,6.07899 20,7.19691 v 9.60669 c 0,1.1179 0,1.6769 -0.2178,2.1043 -0.1917,0.3763 -0.4985,0.6826 -0.8748,0.8743 C 18.48,20 17.921,20 16.8031,20 H 7.19691 C 6.07899,20 5.5192,20 5.0918,19.7822 4.71547,19.5905 4.40973,19.2842 4.21799,18.9079 4,18.4801 4,17.9203 4,16.8002 Z"
|
||||
stroke="#000000"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
style="display:inline" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke:#ffffff;stroke-width:0.838101;stroke-linejoin:round"
|
||||
d="m 352.84046,528.88803 c -3.22669,-1.73681 -30.50855,-27.91507 -60.62636,-58.17393 -48.27401,-48.50006 -54.98796,-55.88217 -56.68725,-62.32871 -2.56679,-9.73752 -0.8264,-18.72921 5.24825,-27.11502 8.6315,-11.91544 26.48529,-16.82146 38.81477,-10.66587 2.95043,1.47303 23.25033,20.4845 45.1109,42.24773 21.86056,21.76322 40.03448,39.22671 40.3865,38.80776 0.35201,-0.41895 33.23427,-39.98485 73.0717,-87.92422 62.72092,-75.4768 73.59894,-87.80127 81.13812,-91.9271 7.39956,-4.04943 9.96286,-4.62014 17.07668,-3.80208 21.67955,2.49306 35.67131,26.45751 26.24377,44.9491 -1.70529,3.34483 -41.79351,52.51958 -89.08494,109.27721 -92.6727,111.22277 -91.5219,110.04524 -107.3927,109.88705 -4.088,-0.0407 -10.07275,-1.4951 -13.29944,-3.23192 z"
|
||||
id="path2"
|
||||
transform="scale(0.03)" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke:#ffffff;stroke-width:0.838101;stroke-linejoin:round"
|
||||
d="m 195.70868,698.11228 c -27.93637,-2.37404 -48.35167,-11.27596 -65.62867,-28.61684 -15.23491,-15.29123 -23.53294,-32.06515 -27.00859,-54.59607 -1.19256,-7.73077 -1.59794,-80.27817 -1.2442,-222.66817 0.46185,-185.91001 0.83864,-212.20504 3.14648,-219.58242 10.44456,-33.38776 37.76921,-59.88854 70.18938,-68.07309 13.71651,-3.46277 435.54403,-3.45635 449.22205,0.007 36.58829,9.26391 62.90519,36.49828 71.72045,74.22085 2.62875,11.24902 2.87053,29.86056 2.87053,220.97074 0,197.21061 -0.17077,209.42455 -3.10538,222.09672 -8.5147,36.76819 -35.67568,64.50713 -72.3237,73.8627 -10.9052,2.7839 -25.87217,3.02005 -212.03951,3.34533 -110.16836,0.1925 -207.27784,-0.24246 -215.79884,-0.96658 z m 418.64543,-67.1917 c 7.95592,-3.6232 16.27613,-13.45601 17.91031,-21.16637 0.73689,-3.47688 1.33982,-99.09936 1.33982,-212.49441 0,-202.8563 -0.0539,-206.27893 -3.3524,-212.76994 -1.84382,-3.62842 -6.16986,-8.84278 -9.61342,-11.58746 -6.18579,-4.93036 -6.60873,-5.0085 -35.20024,-6.50335 -15.91657,-0.83217 -110.77976,-1.27106 -210.8071,-0.97532 -180.66887,0.53417 -181.91761,0.56092 -189.41079,4.05708 -8.82098,4.11569 -12.17627,7.54515 -16.27904,16.63892 -2.8118,6.23235 -3.02931,18.36469 -3.51483,196.05426 -0.29566,108.20723 0.16058,198.57651 1.0642,210.79021 1.42971,19.32469 2.01374,21.94582 6.0768,27.27277 6.33669,8.30783 11.34079,11.12128 22.34305,12.56193 5.27238,0.69037 100.47817,1.15399 211.56844,1.03029 181.32528,-0.20195 202.58499,-0.49941 207.8752,-2.90861 z"
|
||||
id="path3"
|
||||
transform="scale(0.03)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="checkbox-uncheck.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.549375"
|
||||
inkscape:cx="334.01593"
|
||||
inkscape:cy="498.74858"
|
||||
inkscape:window-width="954"
|
||||
inkscape:window-height="1045"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Interface / Checkbox_Check" />
|
||||
<g
|
||||
id="Interface / Checkbox_Check">
|
||||
<path
|
||||
id="Vector"
|
||||
d="m 4,16.8002 v -9.6 C 4,6.08009 4,5.51962 4.21799,5.0918 4.40973,4.71547 4.71547,4.40973 5.0918,4.21799 5.51962,4 6.08009,4 7.2002,4 h 9.6 c 1.1201,0 1.6794,0 2.1072,0.21799 0.3763,0.19174 0.6831,0.49748 0.8748,0.87381 C 20,5.5192 20,6.07899 20,7.19691 v 9.60669 c 0,1.1179 0,1.6769 -0.2178,2.1043 -0.1917,0.3763 -0.4985,0.6826 -0.8748,0.8743 C 18.48,20 17.921,20 16.8031,20 H 7.19691 C 6.07899,20 5.5192,20 5.0918,19.7822 4.71547,19.5905 4.40973,19.2842 4.21799,18.9079 4,18.4801 4,17.9203 4,16.8002 Z"
|
||||
stroke="#000000"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
sodipodi:nodetypes="ssccssccssccssccs" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="m 197.49716,698.05867 c -9.51081,-0.69714 -20.97839,-2.28442 -25.48351,-3.52728 -23.70435,-6.53953 -49.53163,-28.08527 -60.0267,-50.07571 -11.25625,-23.58535 -11.13566,-20.69772 -10.54104,-252.42412 0.50777,-197.87636 0.73145,-210.5848 3.87255,-220.01791 10.98818,-32.99882 33.69637,-55.70701 66.69519,-66.69519 9.43311,-3.1411 22.14155,-3.36478 220.01791,-3.87255 231.59178,-0.59428 228.85346,-0.7082 252.33345,10.49777 15.3635,7.33232 34.26318,25.68875 42.34389,41.12675 12.42777,23.74297 11.88232,14.05756 12.69599,225.43391 0.78039,202.7352 -0.0884,235.68165 -6.71864,254.79581 -9.30619,26.82845 -32.58524,50.14014 -59.23915,59.32223 -6.58312,2.26783 -19.59829,4.44143 -32.76451,5.47182 -24.77577,1.93895 -376.67127,1.90795 -403.18543,-0.0355 z m 416.8785,-66.68447 c 7.41973,-3.34954 16.1894,-13.53942 17.68441,-20.54837 0.76286,-3.5765 1.38703,-99.88158 1.38703,-214.01127 0,-232.19509 1.09029,-213.46397 -13.05322,-224.25175 l -6.68616,-5.09978 H 400.59675 c -209.88362,0 -213.20246,0.0551 -219.15251,3.6405 -3.32285,2.00227 -7.82797,6.50739 -10.01138,10.01137 l -3.96983,6.37088 v 213.11097 213.11097 l 5.07967,6.65981 c 6.00435,7.87211 10.54552,10.61402 20.40383,12.3196 13.0969,2.2659 416.28392,1.0098 421.42913,-1.31293 z"
|
||||
id="path2"
|
||||
transform="scale(0.03)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
viewBox="0 0 16 16"
|
||||
id="svg1"
|
||||
sodipodi:docname="delete-symbolic.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="53.25"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-width="1912"
|
||||
inkscape:window-height="1045"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<style
|
||||
id="current-color-scheme"
|
||||
type="text/css">.ColorScheme-Text { color:#dedede; }</style>
|
||||
</defs>
|
||||
<path
|
||||
d="m6 0c-0.554 0-1 0.446-1 1v1h-4.5c-0.277 0-0.5 0.223-0.5 0.5s0.223 0.5 0.5 0.5h0.49805l0.54688 11.061c0.0612 1.0155 0.90615 1.9395 1.998 1.9395h7.9141c1.0919 0 1.9368-0.92391 1.998-1.9395l0.54688-11.061h0.49805c0.277 0 0.5-0.223 0.5-0.5s-0.223-0.5-0.5-0.5h-4.5v-1c0-0.554-0.446-1-1-1h-3zm0 1h3v1h-3v-1zm-4 2h11l-0.54297 11c-0.03333 0.553-0.446 1-1 1h-7.9141c-0.554 0-0.96667-0.447-1-1l-0.54297-11zm2.5 2c-0.277 0-0.5 0.223-0.5 0.5v7c0 0.277 0.223 0.5 0.5 0.5s0.5-0.223 0.5-0.5v-7c0-0.277-0.223-0.5-0.5-0.5zm3 0c-0.277 0-0.5 0.223-0.5 0.5v7c0 0.277 0.223 0.5 0.5 0.5s0.5-0.223 0.5-0.5v-7c0-0.277-0.223-0.5-0.5-0.5zm3 0c-0.277 0-0.5 0.223-0.5 0.5v7c0 0.277 0.223 0.5 0.5 0.5s0.5-0.223 0.5-0.5v-7c0-0.277-0.223-0.5-0.5-0.5z"
|
||||
style="fill:#ffffff"
|
||||
class="ColorScheme-Text"
|
||||
id="path1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
sodipodi:docname="edit.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="18.826718"
|
||||
inkscape:cx="4.1430482"
|
||||
inkscape:cy="11.101244"
|
||||
inkscape:window-width="1014"
|
||||
inkscape:window-height="1045"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<metadata
|
||||
id="metadata1">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
d="m4 1c-1.108 0-2 0.892-2 2v10c0 1.108 0.892 2 2 2h8c1.108 0 2-0.892 2-2v-5.0488l-1 1v4.0488c0 0.554-0.446 1-1 1h-8c-0.554 0-1-0.446-1-1v-10c0-0.554 0.446-1 1-1h6.0488l1-1z"
|
||||
fill="#dedede"
|
||||
id="path1"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="ColorScheme-Text"
|
||||
d="m14.607 1.6863c-0.78347-0.78347-2.045-0.78347-2.8284 0l3.5355 3.5355c0.78347-0.78347 0.78348-2.045 0-2.8284zm-3.5355 0.70711-7.0711 7.0711-6e-7 3.5355h3.5355l7.0711-7.0711zm0 1.4142 2.1213 2.1213-5.6569 5.6569-2.1213-2.1213z"
|
||||
fill="#dedede"
|
||||
id="path2"
|
||||
style="fill:#ffffff" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
viewBox="0 0 16 16"
|
||||
id="svg2"
|
||||
sodipodi:docname="plus-symbolic.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="53.25"
|
||||
inkscape:cx="7.9906103"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-width="1912"
|
||||
inkscape:window-height="1045"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<rect
|
||||
x="7.5"
|
||||
y="2"
|
||||
width="1"
|
||||
height="12"
|
||||
fill="#dedede"
|
||||
id="rect1"
|
||||
style="fill:#ffffff;stroke:none;stroke-opacity:1" />
|
||||
<rect
|
||||
transform="rotate(90)"
|
||||
x="7.5"
|
||||
y="-14"
|
||||
width="1"
|
||||
height="12"
|
||||
fill="#dedede"
|
||||
id="rect2"
|
||||
style="fill:#ffffff" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M13.969 2.969L6.5 10.438l-4.469-4.47L.97 7.032l5.531 5.53 8.531-8.53z" style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1;marker:none" color="#000" font-weight="400" font-family="sans-serif" overflow="visible" fill="#dedede"/></svg>
|
||||
|
After Width: | Height: | Size: 711 B |
@@ -14,6 +14,7 @@
|
||||
@import url("./styles/tray.css");
|
||||
@import url("./styles/battery-widget.css");
|
||||
@import url("./styles/lock.css");
|
||||
@import url("./styles/todo.css");
|
||||
|
||||
* {
|
||||
all: unset;
|
||||
|
||||
+10
-1
@@ -17,6 +17,7 @@ from modules.panel.components.indicators import (
|
||||
from modules.panel.components.menubar import MenuBar
|
||||
from modules.panel.components.recording_indicator import RecordingIndicator
|
||||
from modules.panel.components.workspace import WorkspaceIndicator
|
||||
from modules.todo.todo_widget import TodoListCapture
|
||||
from utils.roam import modus_service
|
||||
from widgets.mousecapture import MouseCapture
|
||||
from widgets.wayland import WaylandWindow as Window
|
||||
@@ -113,6 +114,9 @@ class Panel(Window):
|
||||
layer="overlay", child_window=NotificationCenter()
|
||||
)
|
||||
|
||||
# Todo List with MouseCapture
|
||||
self.todo_list = TodoListCapture()
|
||||
|
||||
# Notification Center Icon
|
||||
self.notification_icon = Svg(
|
||||
size=22,
|
||||
@@ -127,10 +131,11 @@ class Panel(Window):
|
||||
on_clicked=self.on_notification_icon_clicked,
|
||||
)
|
||||
|
||||
# Clickable DateTime for notification center
|
||||
# Clickable DateTime for todo list
|
||||
self.datetime_button = Button(
|
||||
name="datetime-button",
|
||||
child=DateTime(name="date-time", formatters=["%a %-d %b %I:%M %P"]),
|
||||
on_clicked=self.on_datetime_clicked,
|
||||
)
|
||||
|
||||
self.recording_indicator = RecordingIndicator()
|
||||
@@ -213,6 +218,10 @@ class Panel(Window):
|
||||
self.notification_center.toggle_mousecapture()
|
||||
# Do nothing if no notifications
|
||||
|
||||
def on_datetime_clicked(self, *args):
|
||||
"""Handle datetime button clicks - open todo list."""
|
||||
self.todo_list.toggle_mousecapture()
|
||||
|
||||
def update_notification_icon(self):
|
||||
"""Update the notification icon based on count and DND state."""
|
||||
count = notification_service.count
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# Todo module
|
||||
@@ -0,0 +1,456 @@
|
||||
# Standard library imports
|
||||
from datetime import datetime
|
||||
|
||||
# Fabric imports
|
||||
from fabric.utils import get_relative_path
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.centerbox import CenterBox
|
||||
from fabric.widgets.entry import Entry
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from fabric.widgets.svg import Svg
|
||||
from gi.repository import GLib
|
||||
|
||||
# Local imports
|
||||
from services.todo import todo_service
|
||||
from widgets.mousecapture import MouseCapture
|
||||
from widgets.wayland import WaylandWindow as Window
|
||||
|
||||
|
||||
class TodoItem(Box):
|
||||
"""Individual todo item widget"""
|
||||
|
||||
def __init__(self, todo_data, todo_list_widget, **kwargs):
|
||||
self.todo_data = todo_data
|
||||
self.todo_list_widget = todo_list_widget
|
||||
self.editing = False
|
||||
|
||||
super().__init__(
|
||||
name="todo-item",
|
||||
orientation="h",
|
||||
spacing=8,
|
||||
style_classes=["menu"],
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self._build_ui()
|
||||
|
||||
def _build_ui(self):
|
||||
"""Build the todo item UI"""
|
||||
# Checkbox for completion - using SVG icons
|
||||
checkbox_icon = (
|
||||
"checkbox-check.svg"
|
||||
if self.todo_data["completed"]
|
||||
else "checkbox-uncheck.svg"
|
||||
)
|
||||
self.checkbox_icon = Svg(
|
||||
name="todo-checkbox-icon",
|
||||
size=24,
|
||||
svg_file=get_relative_path(
|
||||
"../../config/assets/icons/todo/" + checkbox_icon
|
||||
),
|
||||
)
|
||||
self.checkbox = Button(
|
||||
name="todo-checkbox",
|
||||
child=self.checkbox_icon,
|
||||
on_clicked=self._toggle_completion,
|
||||
)
|
||||
|
||||
# Todo text (can be converted to entry for editing)
|
||||
text_content = self.todo_data["text"]
|
||||
if self.todo_data["completed"]:
|
||||
text_content = f"<s>{text_content}</s>"
|
||||
|
||||
self.text_label = Label(
|
||||
markup=text_content,
|
||||
name="todo-text",
|
||||
h_align="start",
|
||||
h_expand=True,
|
||||
line_wrap="word-char",
|
||||
style_classes=(
|
||||
["title-widget"]
|
||||
if not self.todo_data["completed"]
|
||||
else ["status-label"]
|
||||
),
|
||||
)
|
||||
|
||||
# Date/time label
|
||||
created_at = datetime.fromisoformat(self.todo_data["created_at"])
|
||||
date_text = created_at.strftime("%b %d, %Y at %I:%M %p")
|
||||
|
||||
self.date_label = Label(
|
||||
label=date_text,
|
||||
name="todo-date",
|
||||
h_align="start",
|
||||
style_classes=["todo-date-text"],
|
||||
)
|
||||
|
||||
self.text_entry = Entry(
|
||||
name="todo-text-entry",
|
||||
text=self.todo_data["text"],
|
||||
h_expand=True,
|
||||
visible=False,
|
||||
)
|
||||
self.text_entry.connect("activate", self._save_edit)
|
||||
|
||||
# Priority indicator
|
||||
# priority_symbols = {"high": "🔴", "medium": "🟡", "low": "🟢"}
|
||||
#
|
||||
# self.priority_label = Label(
|
||||
# label=priority_symbols.get(self.todo_data["priority"], "🟡"),
|
||||
# name="todo-priority-label",
|
||||
# )
|
||||
# self.priority_button = Button(
|
||||
# name="todo-priority",
|
||||
# size=(20, 20),
|
||||
# child=self.priority_label,
|
||||
# on_clicked=self._cycle_priority,
|
||||
# )
|
||||
#
|
||||
# Edit button - using SVG icon
|
||||
self.edit_icon = Svg(
|
||||
name="todo-edit-icon",
|
||||
size=14,
|
||||
svg_file=get_relative_path("../../config/assets/icons/todo/edit.svg"),
|
||||
)
|
||||
self.edit_button = Button(
|
||||
name="todo-edit",
|
||||
child=self.edit_icon,
|
||||
on_clicked=self._start_edit,
|
||||
)
|
||||
|
||||
# Delete button - using SVG icon
|
||||
self.delete_icon = Svg(
|
||||
name="todo-delete-icon",
|
||||
size=14,
|
||||
svg_file=get_relative_path(
|
||||
"../../config/assets/icons/todo/delete-symbolic.svg"
|
||||
),
|
||||
)
|
||||
self.delete_button = Button(
|
||||
name="todo-delete",
|
||||
child=self.delete_icon,
|
||||
on_clicked=self._delete_todo,
|
||||
)
|
||||
|
||||
# Text container that switches between label and entry
|
||||
self.text_container = Box(
|
||||
orientation="v",
|
||||
h_expand=True,
|
||||
children=[
|
||||
Box(orientation="h", children=[self.text_label]),
|
||||
self.date_label,
|
||||
],
|
||||
)
|
||||
|
||||
self.children = [
|
||||
self.checkbox,
|
||||
self.text_container,
|
||||
# self.priority_button,
|
||||
self.edit_button,
|
||||
self.delete_button,
|
||||
]
|
||||
|
||||
def _toggle_completion(self, *_):
|
||||
"""Toggle todo completion status"""
|
||||
todo_service.toggle_todo(self.todo_data["id"])
|
||||
|
||||
def _cycle_priority(self, *_):
|
||||
"""Cycle through priority levels"""
|
||||
priorities = ["low", "medium", "high"]
|
||||
current_index = priorities.index(self.todo_data["priority"])
|
||||
new_priority = priorities[(current_index + 1) % len(priorities)]
|
||||
todo_service.set_priority(self.todo_data["id"], new_priority)
|
||||
|
||||
def _start_edit(self, *_):
|
||||
"""Start editing the todo text"""
|
||||
if self.editing:
|
||||
return
|
||||
|
||||
self.editing = True
|
||||
self.text_container.children = [
|
||||
Box(orientation="h", children=[self.text_entry]),
|
||||
self.date_label,
|
||||
]
|
||||
self.text_entry.set_visible(True)
|
||||
self.text_entry.grab_focus()
|
||||
self.text_entry.set_position(-1) # Move cursor to end
|
||||
|
||||
def _save_edit(self, *_):
|
||||
"""Save the edited todo text"""
|
||||
if not self.editing:
|
||||
return
|
||||
|
||||
new_text = self.text_entry.get_text().strip()
|
||||
if new_text:
|
||||
todo_service.edit_todo(self.todo_data["id"], new_text)
|
||||
|
||||
self._cancel_edit()
|
||||
|
||||
def _cancel_edit(self):
|
||||
"""Cancel editing and revert to label"""
|
||||
self.editing = False
|
||||
self.text_container.children = [
|
||||
Box(orientation="h", children=[self.text_label]),
|
||||
self.date_label,
|
||||
]
|
||||
self.text_entry.set_visible(False)
|
||||
|
||||
def _delete_todo(self, *_):
|
||||
"""Delete this todo"""
|
||||
todo_service.delete_todo(self.todo_data["id"])
|
||||
|
||||
def update_from_data(self, todo_data):
|
||||
"""Update the widget from new todo data"""
|
||||
self.todo_data = todo_data
|
||||
|
||||
# Update checkbox icon by recreating it
|
||||
checkbox_icon = (
|
||||
"checkbox-check.svg" if todo_data["completed"] else "checkbox-uncheck.svg"
|
||||
)
|
||||
new_checkbox_icon = Svg(
|
||||
name="todo-checkbox-icon",
|
||||
size=20,
|
||||
svg_file=get_relative_path(
|
||||
"../../config/assets/icons/todo/" + checkbox_icon
|
||||
),
|
||||
)
|
||||
self.checkbox.set_child(new_checkbox_icon)
|
||||
self.checkbox_icon = new_checkbox_icon
|
||||
|
||||
# Update text and styling with markup
|
||||
text_content = todo_data["text"]
|
||||
if todo_data["completed"]:
|
||||
text_content = f"<s>{text_content}</s>"
|
||||
|
||||
self.text_label.set_markup(text_content)
|
||||
self.text_label.style_classes = (
|
||||
["title-widget"] if not todo_data["completed"] else ["status-label"]
|
||||
)
|
||||
|
||||
# Update date/time
|
||||
created_at = datetime.fromisoformat(todo_data["created_at"])
|
||||
date_text = created_at.strftime("%b %d, %Y at %I:%M %p")
|
||||
self.date_label.set_label(date_text)
|
||||
|
||||
|
||||
class TodoListWidget(Window):
|
||||
"""Main todo list widget window"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
title="modus-todo",
|
||||
anchor="top right",
|
||||
margin="2px 10px 0px 0px",
|
||||
exclusivity="auto",
|
||||
keyboard_mode="on-demand",
|
||||
name="todo-list-window",
|
||||
visible=False, # Back to hidden by default
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.todo_items = {} # Maps todo IDs to TodoItem widgets
|
||||
|
||||
# Register callback with todo service
|
||||
todo_service.add_callback(self._on_todo_event)
|
||||
|
||||
self._build_ui()
|
||||
self._refresh_todos()
|
||||
|
||||
# Add keybinding for escape
|
||||
self.add_keybinding("Escape", self.hide_todo_list)
|
||||
|
||||
def _build_ui(self):
|
||||
"""Build the main UI"""
|
||||
# Header with title and stats
|
||||
self.stats_label = Label(
|
||||
label="",
|
||||
name="todo-stats",
|
||||
style_classes=["status-label"],
|
||||
h_align="start",
|
||||
)
|
||||
|
||||
self.header = Box(
|
||||
name="todo-header",
|
||||
orientation="v",
|
||||
children=[
|
||||
Label(
|
||||
label="Todo List",
|
||||
name="todo-title",
|
||||
style_classes=["title"],
|
||||
h_align="start",
|
||||
),
|
||||
self.stats_label,
|
||||
],
|
||||
)
|
||||
|
||||
# Add new todo section
|
||||
self.new_todo_entry = Entry(
|
||||
name="new-todo-entry",
|
||||
placeholder_text="Add a new task...",
|
||||
h_expand=True,
|
||||
)
|
||||
self.new_todo_entry.connect("activate", self._add_todo)
|
||||
|
||||
# Add button - using SVG icon
|
||||
self.add_icon = Svg(
|
||||
name="add-todo-icon",
|
||||
size=12,
|
||||
svg_file=get_relative_path(
|
||||
"../../config/assets/icons/todo/plus-symbolic.svg"
|
||||
),
|
||||
)
|
||||
self.add_button = Button(
|
||||
name="add-todo-button",
|
||||
child=self.add_icon,
|
||||
on_clicked=self._add_todo,
|
||||
)
|
||||
|
||||
self.add_section = Box(
|
||||
name="todo-add-section",
|
||||
orientation="h",
|
||||
spacing=8,
|
||||
style_classes=["menu"],
|
||||
children=[
|
||||
self.new_todo_entry,
|
||||
self.add_button,
|
||||
],
|
||||
)
|
||||
|
||||
# Todo items container
|
||||
self.todos_container = Box(
|
||||
name="todos-container",
|
||||
orientation="v",
|
||||
spacing=4,
|
||||
)
|
||||
|
||||
# Scrolled window for todos
|
||||
self.scrolled = ScrolledWindow(
|
||||
name="todos-scrolled",
|
||||
min_content_height=300,
|
||||
max_content_height=500,
|
||||
min_content_width=400,
|
||||
child=self.todos_container,
|
||||
policy="automatic",
|
||||
v_expand=True, # Allow vertical expansion
|
||||
)
|
||||
|
||||
# Clear completed button
|
||||
self.clear_button = Button(
|
||||
name="clear-completed-button",
|
||||
label="Clear Completed",
|
||||
style_classes=["status-label"],
|
||||
on_clicked=self._clear_completed,
|
||||
)
|
||||
|
||||
# Main container
|
||||
self.main_container = Box(
|
||||
name="todo-main-container",
|
||||
orientation="v",
|
||||
spacing=8,
|
||||
style_classes=["menu"],
|
||||
children=[
|
||||
self.header,
|
||||
self.add_section,
|
||||
self.scrolled,
|
||||
self.clear_button,
|
||||
],
|
||||
)
|
||||
|
||||
self.children = [self.main_container]
|
||||
|
||||
def _add_todo(self, *_):
|
||||
"""Add a new todo"""
|
||||
text = self.new_todo_entry.get_text().strip()
|
||||
if text:
|
||||
todo_service.add_todo(text)
|
||||
self.new_todo_entry.set_text("")
|
||||
|
||||
def _clear_completed(self, *_):
|
||||
"""Clear all completed todos"""
|
||||
todo_service.clear_completed()
|
||||
|
||||
def _on_todo_event(self, event_type, data=None):
|
||||
"""Handle todo service events via callback"""
|
||||
if event_type == "todos-changed":
|
||||
GLib.idle_add(self._refresh_todos)
|
||||
elif event_type == "todo-added":
|
||||
GLib.idle_add(self._refresh_todos)
|
||||
elif event_type == "todo-deleted":
|
||||
GLib.idle_add(self._refresh_todos)
|
||||
elif event_type in ["todo-toggled", "todo-edited", "todo-priority-changed"]:
|
||||
if data and data["id"] in self.todo_items:
|
||||
GLib.idle_add(
|
||||
lambda: self.todo_items[data["id"]].update_from_data(data)
|
||||
)
|
||||
GLib.idle_add(self._update_stats)
|
||||
|
||||
def _refresh_todos(self, *_):
|
||||
"""Refresh the entire todo list"""
|
||||
# Clear existing items
|
||||
self.todo_items.clear()
|
||||
self.todos_container.children = []
|
||||
|
||||
# Get all todos
|
||||
todos = todo_service.todos
|
||||
|
||||
# Sort todos: incomplete first, then by priority, then by creation date
|
||||
def sort_key(todo):
|
||||
priority_order = {"high": 0, "medium": 1, "low": 2}
|
||||
return (
|
||||
todo["completed"], # False (incomplete) comes before True (completed)
|
||||
priority_order.get(todo["priority"], 1),
|
||||
todo["created_at"],
|
||||
)
|
||||
|
||||
sorted_todos = sorted(todos, key=sort_key)
|
||||
|
||||
# Create todo item widgets
|
||||
for todo in sorted_todos:
|
||||
todo_item = TodoItem(todo, self)
|
||||
self.todo_items[todo["id"]] = todo_item
|
||||
|
||||
# Update container children
|
||||
self.todos_container.children = list(self.todo_items.values())
|
||||
|
||||
# Update stats
|
||||
self._update_stats()
|
||||
|
||||
def _update_stats(self):
|
||||
"""Update the statistics display"""
|
||||
stats = todo_service.get_stats()
|
||||
stats_text = f"{stats['pending']} pending, {stats['completed']} completed"
|
||||
self.stats_label.set_label(stats_text)
|
||||
|
||||
def set_visible(self, visible):
|
||||
"""Override set_visible for debugging"""
|
||||
super().set_visible(visible)
|
||||
|
||||
def hide_todo_list(self, *_):
|
||||
"""Hide the todo list"""
|
||||
if hasattr(self, "_mousecapture_parent"):
|
||||
self._mousecapture_parent.toggle_mousecapture()
|
||||
self.set_visible(False)
|
||||
|
||||
def _init_mousecapture(self, mousecapture):
|
||||
"""Initialize mousecapture parent reference"""
|
||||
self._mousecapture_parent = mousecapture
|
||||
|
||||
def destroy(self):
|
||||
"""Clean up when destroyed"""
|
||||
# Remove callback from todo service
|
||||
todo_service.remove_callback(self._on_todo_event)
|
||||
|
||||
super().destroy()
|
||||
|
||||
|
||||
class TodoListCapture(MouseCapture):
|
||||
"""MouseCapture wrapper for the todo list"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
layer="top",
|
||||
child_window=TodoListWidget(),
|
||||
**kwargs,
|
||||
)
|
||||
@@ -0,0 +1,169 @@
|
||||
# Standard library imports
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# Fabric imports
|
||||
from fabric.core.service import Property, Service, Signal
|
||||
|
||||
# Local imports
|
||||
import config.data as data
|
||||
|
||||
|
||||
class TodoService(Service):
|
||||
"""Service for managing persistent todo list with JSON storage"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._todos = []
|
||||
self._file_path = self._get_todos_file_path()
|
||||
self._load_todos()
|
||||
self._callbacks = []
|
||||
|
||||
def add_callback(self, callback):
|
||||
"""Add a callback function to be notified of changes"""
|
||||
self._callbacks.append(callback)
|
||||
|
||||
def remove_callback(self, callback):
|
||||
"""Remove a callback function"""
|
||||
if callback in self._callbacks:
|
||||
self._callbacks.remove(callback)
|
||||
|
||||
def _notify_callbacks(self, event_type, data=None):
|
||||
"""Notify all registered callbacks of changes"""
|
||||
for callback in self._callbacks:
|
||||
try:
|
||||
callback(event_type, data)
|
||||
except Exception as e:
|
||||
print(f"Error in todo callback: {e}")
|
||||
|
||||
def _get_todos_file_path(self):
|
||||
"""Returns the path to the todos JSON file"""
|
||||
cache_dir = Path(data.CACHE_DIR) / "todos"
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
return cache_dir / "todos.json"
|
||||
|
||||
def _load_todos(self):
|
||||
"""Load todos from JSON file"""
|
||||
try:
|
||||
if self._file_path.exists():
|
||||
with open(self._file_path, "r", encoding="utf-8") as f:
|
||||
self._todos = json.load(f)
|
||||
else:
|
||||
self._todos = []
|
||||
except Exception as e:
|
||||
print(f"Error loading todos: {e}")
|
||||
self._todos = []
|
||||
|
||||
def _save_todos(self):
|
||||
"""Save todos to JSON file"""
|
||||
try:
|
||||
with open(self._file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(self._todos, f, indent=2, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
print(f"Error saving todos: {e}")
|
||||
|
||||
@Property(list, "readable")
|
||||
def todos(self):
|
||||
"""Get all todos"""
|
||||
return self._todos.copy()
|
||||
|
||||
def add_todo(self, text: str, priority: str = "medium") -> dict:
|
||||
"""Add a new todo item"""
|
||||
todo = {
|
||||
"id": str(uuid.uuid4()),
|
||||
"text": text,
|
||||
"completed": False,
|
||||
"priority": priority, # low, medium, high
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"updated_at": datetime.now().isoformat()
|
||||
}
|
||||
self._todos.append(todo)
|
||||
self._save_todos()
|
||||
self._notify_callbacks("todo-added", todo)
|
||||
self._notify_callbacks("todos-changed")
|
||||
return todo
|
||||
|
||||
def delete_todo(self, todo_id: str) -> bool:
|
||||
"""Delete a todo item by ID"""
|
||||
for i, todo in enumerate(self._todos):
|
||||
if todo["id"] == todo_id:
|
||||
deleted_todo = self._todos.pop(i)
|
||||
self._save_todos()
|
||||
self._notify_callbacks("todo-deleted", deleted_todo)
|
||||
self._notify_callbacks("todos-changed")
|
||||
return True
|
||||
return False
|
||||
|
||||
def toggle_todo(self, todo_id: str) -> bool:
|
||||
"""Toggle completion status of a todo item"""
|
||||
for todo in self._todos:
|
||||
if todo["id"] == todo_id:
|
||||
todo["completed"] = not todo["completed"]
|
||||
todo["updated_at"] = datetime.now().isoformat()
|
||||
self._save_todos()
|
||||
self._notify_callbacks("todo-toggled", todo)
|
||||
self._notify_callbacks("todos-changed")
|
||||
return True
|
||||
return False
|
||||
|
||||
def edit_todo(self, todo_id: str, new_text: str) -> bool:
|
||||
"""Edit the text of a todo item"""
|
||||
for todo in self._todos:
|
||||
if todo["id"] == todo_id:
|
||||
todo["text"] = new_text
|
||||
todo["updated_at"] = datetime.now().isoformat()
|
||||
self._save_todos()
|
||||
self._notify_callbacks("todo-edited", todo)
|
||||
self._notify_callbacks("todos-changed")
|
||||
return True
|
||||
return False
|
||||
|
||||
def set_priority(self, todo_id: str, priority: str) -> bool:
|
||||
"""Set the priority of a todo item"""
|
||||
if priority not in ["low", "medium", "high"]:
|
||||
return False
|
||||
|
||||
for todo in self._todos:
|
||||
if todo["id"] == todo_id:
|
||||
todo["priority"] = priority
|
||||
todo["updated_at"] = datetime.now().isoformat()
|
||||
self._save_todos()
|
||||
self._notify_callbacks("todo-priority-changed", todo)
|
||||
self._notify_callbacks("todos-changed")
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_todo(self, todo_id: str) -> dict | None:
|
||||
"""Get a specific todo by ID"""
|
||||
for todo in self._todos:
|
||||
if todo["id"] == todo_id:
|
||||
return todo.copy()
|
||||
return None
|
||||
|
||||
def clear_completed(self):
|
||||
"""Remove all completed todos"""
|
||||
initial_count = len(self._todos)
|
||||
self._todos = [todo for todo in self._todos if not todo["completed"]]
|
||||
if len(self._todos) < initial_count:
|
||||
self._save_todos()
|
||||
self._notify_callbacks("todos-changed")
|
||||
|
||||
def get_stats(self) -> dict:
|
||||
"""Get todo statistics"""
|
||||
total = len(self._todos)
|
||||
completed = sum(1 for todo in self._todos if todo["completed"])
|
||||
pending = total - completed
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"completed": completed,
|
||||
"pending": pending,
|
||||
"completion_rate": (completed / total * 100) if total > 0 else 0
|
||||
}
|
||||
|
||||
|
||||
# Global service instance
|
||||
todo_service = TodoService()
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
/* Todo List Styles - matching control center design */
|
||||
|
||||
#todo-list-window {
|
||||
/* background-color: alpha(#fff, 0.05); */
|
||||
border-radius: 12px;
|
||||
box-shadow: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#todo-main-container {
|
||||
box-shadow: inset 0 0 0 1px alpha(#aaa, 0.4);
|
||||
border-radius: 12px;
|
||||
padding: 12px; /* Increased padding */
|
||||
min-height: 500px; /* Ensure minimum height */
|
||||
min-width: 350px; /* Ensure minimum width */
|
||||
}
|
||||
|
||||
#todo-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#todo-title {
|
||||
font-size: 16px;
|
||||
font-family: "SF Pro Rounded";
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#todo-stats {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #999;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
/* Add new todo section */
|
||||
#todo-add-section {
|
||||
box-shadow: inset 0 0 0 0.5px alpha(#aaa, 0.4);
|
||||
border: 1px solid alpha(#111, 0.4);
|
||||
border-radius: 12px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 8px;
|
||||
min-height: 40px; /* Ensure minimum height */
|
||||
}
|
||||
|
||||
#new-todo-entry {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
font-family: "SF Pro Rounded";
|
||||
}
|
||||
|
||||
#new-todo-entry:focus {
|
||||
border-color: #007aff;
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.3);
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
#add-todo-button {
|
||||
/* background-color: #007aff; */
|
||||
/* border: 1px solid #007aff; */
|
||||
border-radius: 6px;
|
||||
min-width: 20px;
|
||||
min-height: 12px;
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#add-todo-button:hover {
|
||||
/* background-color: #0056cc; */
|
||||
/* border-color: #0056cc; */
|
||||
}
|
||||
|
||||
/* Todo items container */
|
||||
#todos-scrolled {
|
||||
background-color: transparent;
|
||||
min-height: 200px; /* Minimum height for content */
|
||||
}
|
||||
|
||||
#todos-container {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
/* Individual todo items */
|
||||
#todo-item {
|
||||
background-color: alpha(#000, 0.2); /* More visible background */
|
||||
border: 1px solid alpha(#111, 0.4);
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
margin: 4px 0;
|
||||
min-height: 40px; /* Ensure minimum height */
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
#todo-item:hover {
|
||||
background-color: alpha(#fff, 0.12);
|
||||
}
|
||||
|
||||
/* Todo item controls */
|
||||
#todo-checkbox {
|
||||
background-color: transparent;
|
||||
border-radius: 50%; /* Circular appearance */
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
#todo-checkbox:hover {
|
||||
border-color: #007aff;
|
||||
}
|
||||
|
||||
/* SVG icon styling for checkboxes and buttons */
|
||||
#todo-checkbox-icon {
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
#todo-edit-icon,
|
||||
#todo-delete-icon,
|
||||
#add-todo-icon {
|
||||
color: #ffffff;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
#todo-edit-icon:hover,
|
||||
#todo-delete-icon:hover,
|
||||
#add-todo-icon:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#todo-text {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
font-family: "SF Pro Rounded";
|
||||
}
|
||||
|
||||
#todo-date {
|
||||
font-size: 10px;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
font-family: "SF Pro Rounded";
|
||||
margin-top: 2px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.todo-date-text {
|
||||
color: #999999;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
#todo-text-entry {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-family: "SF Pro Rounded";
|
||||
}
|
||||
|
||||
#todo-text-entry:focus {
|
||||
border-color: #007aff;
|
||||
box-shadow: 0 0 0 1px rgba(0, 122, 255, 0.3);
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* Priority, edit, delete buttons */
|
||||
#todo-priority,
|
||||
#todo-edit,
|
||||
#todo-delete {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
margin: 0 2px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
#todo-priority:hover,
|
||||
#todo-edit:hover,
|
||||
#todo-delete:hover {
|
||||
background-color: alpha(#fff, 0.1);
|
||||
}
|
||||
|
||||
#todo-priority-label,
|
||||
#todo-edit-label,
|
||||
#todo-delete-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Clear completed button */
|
||||
#clear-completed-button {
|
||||
background-color: alpha(#fff, 0.1);
|
||||
border: 1px solid alpha(#999, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 8px 16px;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-family: "SF Pro Rounded";
|
||||
margin-top: 8px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
#clear-completed-button:hover {
|
||||
background-color: alpha(#fff, 0.15);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Completed todos styling */
|
||||
#todo-item.completed {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
#todo-item.completed #todo-text {
|
||||
text-decoration: line-through;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* Priority styling */
|
||||
.priority-high #todo-priority-label {
|
||||
color: #ff4444;
|
||||
}
|
||||
|
||||
.priority-medium #todo-priority-label {
|
||||
color: #ffaa00;
|
||||
}
|
||||
|
||||
.priority-low #todo-priority-label {
|
||||
color: #44ff44;
|
||||
}
|
||||
|
||||
/* Scrollbar styling to match control center */
|
||||
#todos-scrolled scrollbar {
|
||||
background-color: transparent;
|
||||
border-radius: 8px;
|
||||
margin: 2px;
|
||||
min-width: 8px;
|
||||
}
|
||||
|
||||
#todos-scrolled scrollbar slider {
|
||||
background-color: alpha(#fff, 0.3);
|
||||
border-radius: 4px;
|
||||
min-width: 6px;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
#todos-scrolled scrollbar slider:hover {
|
||||
background-color: alpha(#fff, 0.5);
|
||||
}
|
||||
|
||||
#todos-scrolled scrollbar slider:active {
|
||||
background-color: alpha(#fff, 0.7);
|
||||
}
|
||||
Reference in new issue
Block a user