pop-note 0.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.
- pop_note-0.1.0/PKG-INFO +52 -0
- pop_note-0.1.0/README.md +39 -0
- pop_note-0.1.0/pop_note/__init__.py +0 -0
- pop_note-0.1.0/pop_note/main.py +366 -0
- pop_note-0.1.0/pop_note.egg-info/PKG-INFO +52 -0
- pop_note-0.1.0/pop_note.egg-info/SOURCES.txt +10 -0
- pop_note-0.1.0/pop_note.egg-info/dependency_links.txt +1 -0
- pop_note-0.1.0/pop_note.egg-info/entry_points.txt +2 -0
- pop_note-0.1.0/pop_note.egg-info/requires.txt +3 -0
- pop_note-0.1.0/pop_note.egg-info/top_level.txt +1 -0
- pop_note-0.1.0/pyproject.toml +29 -0
- pop_note-0.1.0/setup.cfg +4 -0
pop_note-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pop-note
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A single note you can pop up and hide with a keybinding.
|
|
5
|
+
Author-email: "@readwithai" <talwrii@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/talwrii/pop-note
|
|
7
|
+
Project-URL: Repository, https://github.com/talwrii/pop-note
|
|
8
|
+
Project-URL: Issues, https://github.com/talwrii/pop-note/issues
|
|
9
|
+
Keywords: notes,popup,quake,tkinter
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: tomli; python_version < "3.11"
|
|
13
|
+
|
|
14
|
+
# pop-note
|
|
15
|
+
A single editable note that you can pop up and hide with a keybinding. Requires X11 (normally used with linux) (easy fix tho)
|
|
16
|
+
|
|
17
|
+
Unreviewed ai-generated code.
|
|
18
|
+
|
|
19
|
+
## Motivation
|
|
20
|
+
I don't have any window space and my computer only accepts two monitors. I don't want to change anything about my editor, or note taking app.
|
|
21
|
+
|
|
22
|
+
Philosophy, don't separate process and capture.
|
|
23
|
+
|
|
24
|
+
## Alternatives
|
|
25
|
+
So many alternatives:
|
|
26
|
+
|
|
27
|
+
* Buy a monitor or a new computer and put it somewhere. Monitors are basically free nowerdays.
|
|
28
|
+
* Put your notes on another desktop and toggle desktops. Get it to launch at start up
|
|
29
|
+
* Have a shortcut to raise you editor / editor window (run-or-raise)
|
|
30
|
+
* Using something capture based like org mode, where you capture the information and process it latter
|
|
31
|
+
* Go to your daily not in obsidan and use the back button
|
|
32
|
+
|
|
33
|
+
But for now I just want this and I am feeling lazy.
|
|
34
|
+
|
|
35
|
+
This was kind of influenced by "quake style pop-down" terminals. But I am too lazy to implement a pop-down syle animation.
|
|
36
|
+
|
|
37
|
+
## Feature requests
|
|
38
|
+
No.
|
|
39
|
+
|
|
40
|
+
This is one of thos apps which is designed to have no features. If you want somethign clever, use one of the approaches above.
|
|
41
|
+
|
|
42
|
+
I might however add the features I want.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Caveats
|
|
46
|
+
Only works with x11 because tkinter window raising does not work reliably with KDE so I had to use wmctrl to raise windows. Apparently there are different tools for every wayland compositor so if you use one of those you could special case this and send it to me.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
pipx install node-
|
|
50
|
+
|
|
51
|
+
## About me
|
|
52
|
+
I am @readwith. If you are interested in note-taking [read this](https://readwithai.substack.com/p/note-taking-with-obsidian-much-of)
|
pop_note-0.1.0/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# pop-note
|
|
2
|
+
A single editable note that you can pop up and hide with a keybinding. Requires X11 (normally used with linux) (easy fix tho)
|
|
3
|
+
|
|
4
|
+
Unreviewed ai-generated code.
|
|
5
|
+
|
|
6
|
+
## Motivation
|
|
7
|
+
I don't have any window space and my computer only accepts two monitors. I don't want to change anything about my editor, or note taking app.
|
|
8
|
+
|
|
9
|
+
Philosophy, don't separate process and capture.
|
|
10
|
+
|
|
11
|
+
## Alternatives
|
|
12
|
+
So many alternatives:
|
|
13
|
+
|
|
14
|
+
* Buy a monitor or a new computer and put it somewhere. Monitors are basically free nowerdays.
|
|
15
|
+
* Put your notes on another desktop and toggle desktops. Get it to launch at start up
|
|
16
|
+
* Have a shortcut to raise you editor / editor window (run-or-raise)
|
|
17
|
+
* Using something capture based like org mode, where you capture the information and process it latter
|
|
18
|
+
* Go to your daily not in obsidan and use the back button
|
|
19
|
+
|
|
20
|
+
But for now I just want this and I am feeling lazy.
|
|
21
|
+
|
|
22
|
+
This was kind of influenced by "quake style pop-down" terminals. But I am too lazy to implement a pop-down syle animation.
|
|
23
|
+
|
|
24
|
+
## Feature requests
|
|
25
|
+
No.
|
|
26
|
+
|
|
27
|
+
This is one of thos apps which is designed to have no features. If you want somethign clever, use one of the approaches above.
|
|
28
|
+
|
|
29
|
+
I might however add the features I want.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Caveats
|
|
33
|
+
Only works with x11 because tkinter window raising does not work reliably with KDE so I had to use wmctrl to raise windows. Apparently there are different tools for every wayland compositor so if you use one of those you could special case this and send it to me.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
pipx install node-
|
|
37
|
+
|
|
38
|
+
## About me
|
|
39
|
+
I am @readwith. If you are interested in note-taking [read this](https://readwithai.substack.com/p/note-taking-with-obsidian-much-of)
|
|
File without changes
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""pop-note: a single note you can pop up and hide with a keybinding."""
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import socket
|
|
6
|
+
import threading
|
|
7
|
+
import datetime
|
|
8
|
+
import tkinter as tk
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import subprocess
|
|
12
|
+
try:
|
|
13
|
+
import tomllib # Python 3.11+
|
|
14
|
+
except ModuleNotFoundError:
|
|
15
|
+
import tomli as tomllib
|
|
16
|
+
|
|
17
|
+
CONFIG_PATH = Path.home() / ".config" / "pop-note" / "config.toml"
|
|
18
|
+
DEFAULT_NOTE_PATH = Path.home() / "notes" / "pop-note.md"
|
|
19
|
+
DEFAULT_VERSIONS_DIR = Path.home() / ".local" / "share" / "pop-note" / "versions"
|
|
20
|
+
SOCKET_PATH = Path(f"/tmp/pop-note-{os.getuid()}.sock")
|
|
21
|
+
PIDFILE_PATH = Path(f"/tmp/pop-note-{os.getuid()}.pid")
|
|
22
|
+
LOG_DIR = Path.home() / ".local" / "share" / "pop-note" / "logs"
|
|
23
|
+
STATE_PATH = Path.home() / ".local" / "share" / "pop-note" / "state.toml"
|
|
24
|
+
|
|
25
|
+
VERSION = "8"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_config():
|
|
29
|
+
note_path = DEFAULT_NOTE_PATH
|
|
30
|
+
versions_dir = DEFAULT_VERSIONS_DIR
|
|
31
|
+
if CONFIG_PATH.exists():
|
|
32
|
+
with open(CONFIG_PATH, "rb") as f:
|
|
33
|
+
cfg = tomllib.load(f)
|
|
34
|
+
if "note_path" in cfg:
|
|
35
|
+
note_path = Path(os.path.expanduser(cfg["note_path"]))
|
|
36
|
+
if "versions_dir" in cfg:
|
|
37
|
+
versions_dir = Path(os.path.expanduser(cfg["versions_dir"]))
|
|
38
|
+
note_path.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
versions_dir.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
if not note_path.exists():
|
|
41
|
+
note_path.write_text("")
|
|
42
|
+
return note_path, versions_dir
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _read_pid():
|
|
46
|
+
try:
|
|
47
|
+
return int(PIDFILE_PATH.read_text().strip())
|
|
48
|
+
except (FileNotFoundError, ValueError):
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _pid_alive(pid):
|
|
53
|
+
if pid is None:
|
|
54
|
+
return False
|
|
55
|
+
try:
|
|
56
|
+
os.kill(pid, 0)
|
|
57
|
+
return True
|
|
58
|
+
except (ProcessLookupError, PermissionError):
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _cleanup_stale():
|
|
63
|
+
for p in (SOCKET_PATH, PIDFILE_PATH):
|
|
64
|
+
try:
|
|
65
|
+
p.unlink()
|
|
66
|
+
except FileNotFoundError:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _ask_version():
|
|
71
|
+
"""Ask the running daemon what version it is. Returns string or None."""
|
|
72
|
+
if not SOCKET_PATH.exists():
|
|
73
|
+
return None
|
|
74
|
+
try:
|
|
75
|
+
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
76
|
+
s.settimeout(0.5)
|
|
77
|
+
s.connect(str(SOCKET_PATH))
|
|
78
|
+
s.sendall(b"version\n")
|
|
79
|
+
data = s.recv(64)
|
|
80
|
+
s.close()
|
|
81
|
+
return data.decode().strip()
|
|
82
|
+
except (ConnectionRefusedError, FileNotFoundError, socket.timeout, OSError):
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def try_toggle_existing():
|
|
87
|
+
"""If a matching-version daemon is running, toggle it and return True.
|
|
88
|
+
If a stale-version daemon is running, kill it and return False so we restart."""
|
|
89
|
+
pid = _read_pid()
|
|
90
|
+
if not _pid_alive(pid):
|
|
91
|
+
_cleanup_stale()
|
|
92
|
+
return False
|
|
93
|
+
if not SOCKET_PATH.exists():
|
|
94
|
+
return False
|
|
95
|
+
running_version = _ask_version()
|
|
96
|
+
if running_version != VERSION:
|
|
97
|
+
print(f"pop-note: running daemon is version {running_version!r}, "
|
|
98
|
+
f"need {VERSION!r} — restarting", file=sys.stderr)
|
|
99
|
+
kill_existing()
|
|
100
|
+
# Give it a moment to clean up
|
|
101
|
+
import time
|
|
102
|
+
time.sleep(0.2)
|
|
103
|
+
return False
|
|
104
|
+
try:
|
|
105
|
+
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
106
|
+
s.settimeout(0.5)
|
|
107
|
+
s.connect(str(SOCKET_PATH))
|
|
108
|
+
s.sendall(b"toggle\n")
|
|
109
|
+
s.close()
|
|
110
|
+
return True
|
|
111
|
+
except (ConnectionRefusedError, FileNotFoundError, socket.timeout, OSError):
|
|
112
|
+
_cleanup_stale()
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def kill_existing():
|
|
117
|
+
pid = _read_pid()
|
|
118
|
+
if not _pid_alive(pid):
|
|
119
|
+
_cleanup_stale()
|
|
120
|
+
print("pop-note: not running")
|
|
121
|
+
return
|
|
122
|
+
try:
|
|
123
|
+
os.kill(pid, 15)
|
|
124
|
+
print(f"pop-note: killed pid {pid}")
|
|
125
|
+
except ProcessLookupError:
|
|
126
|
+
pass
|
|
127
|
+
_cleanup_stale()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def daemonise(log_path):
|
|
131
|
+
"""Standard double-fork daemonisation. Redirects stdout/stderr to log_path."""
|
|
132
|
+
if os.fork() > 0:
|
|
133
|
+
os._exit(0)
|
|
134
|
+
os.setsid()
|
|
135
|
+
if os.fork() > 0:
|
|
136
|
+
os._exit(0)
|
|
137
|
+
devnull = os.open(os.devnull, os.O_RDONLY)
|
|
138
|
+
os.dup2(devnull, 0)
|
|
139
|
+
os.close(devnull)
|
|
140
|
+
log_fd = os.open(str(log_path), os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644)
|
|
141
|
+
os.dup2(log_fd, 1)
|
|
142
|
+
os.dup2(log_fd, 2)
|
|
143
|
+
os.close(log_fd)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def latest_log():
|
|
147
|
+
if not LOG_DIR.exists():
|
|
148
|
+
return None
|
|
149
|
+
logs = sorted(LOG_DIR.glob("*.log"))
|
|
150
|
+
return logs[-1] if logs else None
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def show_last_log():
|
|
154
|
+
log = latest_log()
|
|
155
|
+
if log is None:
|
|
156
|
+
print("pop-note: no logs found")
|
|
157
|
+
return
|
|
158
|
+
print(f"# {log}")
|
|
159
|
+
sys.stdout.write(log.read_text())
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class PopNote:
|
|
163
|
+
def __init__(self, note_path, versions_dir):
|
|
164
|
+
self.note_path = note_path
|
|
165
|
+
self.versions_dir = versions_dir
|
|
166
|
+
self.visible = False
|
|
167
|
+
|
|
168
|
+
self.root = tk.Tk()
|
|
169
|
+
self._wm_title = f"pop-note-{os.getpid()}"
|
|
170
|
+
self.root.title(self._wm_title)
|
|
171
|
+
self.root.geometry("600x400")
|
|
172
|
+
self.text = tk.Text(self.root, wrap="word", font=("monospace", 11),
|
|
173
|
+
undo=True)
|
|
174
|
+
self.text.pack(fill="both", expand=True)
|
|
175
|
+
|
|
176
|
+
# Override window close to hide instead of quit
|
|
177
|
+
self.root.protocol("WM_DELETE_WINDOW", self.hide)
|
|
178
|
+
self.root.bind("<Escape>", lambda e: self.hide())
|
|
179
|
+
self.root.bind("<Configure>", self._on_configure)
|
|
180
|
+
|
|
181
|
+
# Start hidden — first toggle invocation will show it
|
|
182
|
+
self.root.withdraw()
|
|
183
|
+
|
|
184
|
+
# Listen for toggle messages
|
|
185
|
+
self._start_socket_thread()
|
|
186
|
+
|
|
187
|
+
def _start_socket_thread(self):
|
|
188
|
+
if SOCKET_PATH.exists():
|
|
189
|
+
try:
|
|
190
|
+
SOCKET_PATH.unlink()
|
|
191
|
+
except FileNotFoundError:
|
|
192
|
+
pass
|
|
193
|
+
srv = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
194
|
+
srv.bind(str(SOCKET_PATH))
|
|
195
|
+
srv.listen(4)
|
|
196
|
+
self._srv = srv
|
|
197
|
+
|
|
198
|
+
def loop():
|
|
199
|
+
while True:
|
|
200
|
+
try:
|
|
201
|
+
conn, _ = srv.accept()
|
|
202
|
+
data = conn.recv(64)
|
|
203
|
+
if b"version" in data:
|
|
204
|
+
try:
|
|
205
|
+
conn.sendall(VERSION.encode() + b"\n")
|
|
206
|
+
except OSError:
|
|
207
|
+
pass
|
|
208
|
+
elif b"toggle" in data:
|
|
209
|
+
self.root.after(0, self.toggle)
|
|
210
|
+
conn.close()
|
|
211
|
+
except OSError:
|
|
212
|
+
break
|
|
213
|
+
|
|
214
|
+
t = threading.Thread(target=loop, daemon=True)
|
|
215
|
+
t.start()
|
|
216
|
+
|
|
217
|
+
def _snapshot(self, label):
|
|
218
|
+
ts = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
|
|
219
|
+
content = self.text.get("1.0", "end-1c") if self.visible else self.note_path.read_text()
|
|
220
|
+
path = self.versions_dir / f"{ts}-{label}.md"
|
|
221
|
+
path.write_text(content)
|
|
222
|
+
|
|
223
|
+
def _load_into_text(self):
|
|
224
|
+
self.text.delete("1.0", "end")
|
|
225
|
+
self.text.insert("1.0", self.note_path.read_text())
|
|
226
|
+
|
|
227
|
+
def _save_live(self):
|
|
228
|
+
content = self.text.get("1.0", "end-1c")
|
|
229
|
+
self.note_path.write_text(content)
|
|
230
|
+
|
|
231
|
+
def _on_configure(self, event):
|
|
232
|
+
# Configure fires for child widgets too — only act on the toplevel.
|
|
233
|
+
if event.widget is not self.root:
|
|
234
|
+
return
|
|
235
|
+
if not self.visible:
|
|
236
|
+
return
|
|
237
|
+
self._save_geometry()
|
|
238
|
+
|
|
239
|
+
def _load_geometry(self):
|
|
240
|
+
try:
|
|
241
|
+
with open(STATE_PATH, "rb") as f:
|
|
242
|
+
state = tomllib.load(f)
|
|
243
|
+
return state.get("geometry")
|
|
244
|
+
except (FileNotFoundError, Exception):
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
def _save_geometry(self):
|
|
248
|
+
try:
|
|
249
|
+
STATE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
250
|
+
geom = self.root.winfo_geometry() # "WxH+X+Y"
|
|
251
|
+
STATE_PATH.write_text(f'geometry = "{geom}"\n')
|
|
252
|
+
except Exception as e:
|
|
253
|
+
print(f"failed to save geometry: {e}", flush=True)
|
|
254
|
+
|
|
255
|
+
def _wmctrl_raise(self):
|
|
256
|
+
try:
|
|
257
|
+
# Match by exact title (-F) and activate (-a)
|
|
258
|
+
r = subprocess.run(
|
|
259
|
+
["wmctrl", "-F", "-a", self._wm_title],
|
|
260
|
+
capture_output=True, text=True,
|
|
261
|
+
)
|
|
262
|
+
if r.returncode != 0:
|
|
263
|
+
print(f"wmctrl raise failed rc={r.returncode} "
|
|
264
|
+
f"stderr={r.stderr.strip()}", flush=True)
|
|
265
|
+
# Fallback: list windows so we can see what's there
|
|
266
|
+
lst = subprocess.run(["wmctrl", "-l"], capture_output=True, text=True)
|
|
267
|
+
print(f"wmctrl -l:\n{lst.stdout}", flush=True)
|
|
268
|
+
self.root.lift()
|
|
269
|
+
self.root.focus_force()
|
|
270
|
+
except FileNotFoundError:
|
|
271
|
+
print("wmctrl not installed; falling back to tk lift", flush=True)
|
|
272
|
+
self.root.lift()
|
|
273
|
+
self.root.focus_force()
|
|
274
|
+
|
|
275
|
+
def _center_on_pointer(self):
|
|
276
|
+
# Center the window on whichever monitor the mouse is currently on.
|
|
277
|
+
self.root.update_idletasks()
|
|
278
|
+
w = self.root.winfo_width() or 600
|
|
279
|
+
h = self.root.winfo_height() or 400
|
|
280
|
+
px = self.root.winfo_pointerx()
|
|
281
|
+
py = self.root.winfo_pointery()
|
|
282
|
+
x = px - w // 2
|
|
283
|
+
y = py - h // 2
|
|
284
|
+
self.root.geometry(f"{w}x{h}+{x}+{y}")
|
|
285
|
+
|
|
286
|
+
def show(self):
|
|
287
|
+
if self.visible:
|
|
288
|
+
return
|
|
289
|
+
self._load_into_text()
|
|
290
|
+
self._snapshot("show")
|
|
291
|
+
self.visible = True
|
|
292
|
+
self.root.deiconify()
|
|
293
|
+
saved = self._load_geometry()
|
|
294
|
+
if saved:
|
|
295
|
+
self.root.geometry(saved)
|
|
296
|
+
else:
|
|
297
|
+
self._center_on_pointer()
|
|
298
|
+
self.root.update_idletasks()
|
|
299
|
+
self._wmctrl_raise()
|
|
300
|
+
self.text.focus_set()
|
|
301
|
+
|
|
302
|
+
def hide(self):
|
|
303
|
+
if not self.visible:
|
|
304
|
+
return
|
|
305
|
+
self._save_live()
|
|
306
|
+
self._save_geometry()
|
|
307
|
+
self._snapshot("hide")
|
|
308
|
+
self.visible = False
|
|
309
|
+
self.root.withdraw()
|
|
310
|
+
|
|
311
|
+
def toggle(self):
|
|
312
|
+
if self.visible:
|
|
313
|
+
self.hide()
|
|
314
|
+
else:
|
|
315
|
+
self.show()
|
|
316
|
+
|
|
317
|
+
def run(self):
|
|
318
|
+
try:
|
|
319
|
+
self.root.mainloop()
|
|
320
|
+
finally:
|
|
321
|
+
try:
|
|
322
|
+
self._srv.close()
|
|
323
|
+
except Exception:
|
|
324
|
+
pass
|
|
325
|
+
try:
|
|
326
|
+
SOCKET_PATH.unlink()
|
|
327
|
+
except FileNotFoundError:
|
|
328
|
+
pass
|
|
329
|
+
try:
|
|
330
|
+
PIDFILE_PATH.unlink()
|
|
331
|
+
except FileNotFoundError:
|
|
332
|
+
pass
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def main():
|
|
336
|
+
if "--kill" in sys.argv[1:]:
|
|
337
|
+
kill_existing()
|
|
338
|
+
return
|
|
339
|
+
|
|
340
|
+
if "--last-log" in sys.argv[1:]:
|
|
341
|
+
show_last_log()
|
|
342
|
+
return
|
|
343
|
+
|
|
344
|
+
if try_toggle_existing():
|
|
345
|
+
return
|
|
346
|
+
|
|
347
|
+
# No live instance — start one as a daemon and toggle it open.
|
|
348
|
+
note_path, versions_dir = load_config()
|
|
349
|
+
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
350
|
+
ts = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
|
|
351
|
+
log_path = LOG_DIR / f"{ts}.log"
|
|
352
|
+
daemonise(log_path)
|
|
353
|
+
PIDFILE_PATH.write_text(str(os.getpid()))
|
|
354
|
+
print(f"pop-note daemon started, pid={os.getpid()}", flush=True)
|
|
355
|
+
try:
|
|
356
|
+
app = PopNote(note_path, versions_dir)
|
|
357
|
+
app.root.after(50, app.show)
|
|
358
|
+
app.run()
|
|
359
|
+
except Exception:
|
|
360
|
+
import traceback
|
|
361
|
+
traceback.print_exc()
|
|
362
|
+
raise
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
if __name__ == "__main__":
|
|
366
|
+
main()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pop-note
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A single note you can pop up and hide with a keybinding.
|
|
5
|
+
Author-email: "@readwithai" <talwrii@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/talwrii/pop-note
|
|
7
|
+
Project-URL: Repository, https://github.com/talwrii/pop-note
|
|
8
|
+
Project-URL: Issues, https://github.com/talwrii/pop-note/issues
|
|
9
|
+
Keywords: notes,popup,quake,tkinter
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: tomli; python_version < "3.11"
|
|
13
|
+
|
|
14
|
+
# pop-note
|
|
15
|
+
A single editable note that you can pop up and hide with a keybinding. Requires X11 (normally used with linux) (easy fix tho)
|
|
16
|
+
|
|
17
|
+
Unreviewed ai-generated code.
|
|
18
|
+
|
|
19
|
+
## Motivation
|
|
20
|
+
I don't have any window space and my computer only accepts two monitors. I don't want to change anything about my editor, or note taking app.
|
|
21
|
+
|
|
22
|
+
Philosophy, don't separate process and capture.
|
|
23
|
+
|
|
24
|
+
## Alternatives
|
|
25
|
+
So many alternatives:
|
|
26
|
+
|
|
27
|
+
* Buy a monitor or a new computer and put it somewhere. Monitors are basically free nowerdays.
|
|
28
|
+
* Put your notes on another desktop and toggle desktops. Get it to launch at start up
|
|
29
|
+
* Have a shortcut to raise you editor / editor window (run-or-raise)
|
|
30
|
+
* Using something capture based like org mode, where you capture the information and process it latter
|
|
31
|
+
* Go to your daily not in obsidan and use the back button
|
|
32
|
+
|
|
33
|
+
But for now I just want this and I am feeling lazy.
|
|
34
|
+
|
|
35
|
+
This was kind of influenced by "quake style pop-down" terminals. But I am too lazy to implement a pop-down syle animation.
|
|
36
|
+
|
|
37
|
+
## Feature requests
|
|
38
|
+
No.
|
|
39
|
+
|
|
40
|
+
This is one of thos apps which is designed to have no features. If you want somethign clever, use one of the approaches above.
|
|
41
|
+
|
|
42
|
+
I might however add the features I want.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Caveats
|
|
46
|
+
Only works with x11 because tkinter window raising does not work reliably with KDE so I had to use wmctrl to raise windows. Apparently there are different tools for every wayland compositor so if you use one of those you could special case this and send it to me.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
pipx install node-
|
|
50
|
+
|
|
51
|
+
## About me
|
|
52
|
+
I am @readwith. If you are interested in note-taking [read this](https://readwithai.substack.com/p/note-taking-with-obsidian-much-of)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
pop_note/__init__.py
|
|
4
|
+
pop_note/main.py
|
|
5
|
+
pop_note.egg-info/PKG-INFO
|
|
6
|
+
pop_note.egg-info/SOURCES.txt
|
|
7
|
+
pop_note.egg-info/dependency_links.txt
|
|
8
|
+
pop_note.egg-info/entry_points.txt
|
|
9
|
+
pop_note.egg-info/requires.txt
|
|
10
|
+
pop_note.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pop_note
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pop-note"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A single note you can pop up and hide with a keybinding."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.9"
|
|
7
|
+
keywords = ["notes", "popup", "quake", "tkinter"]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"tomli; python_version < '3.11'",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[[project.authors]]
|
|
13
|
+
name = "@readwithai"
|
|
14
|
+
email = "talwrii@gmail.com"
|
|
15
|
+
|
|
16
|
+
[project.license]
|
|
17
|
+
file = "LICENSE"
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/talwrii/pop-note"
|
|
21
|
+
Repository = "https://github.com/talwrii/pop-note"
|
|
22
|
+
Issues = "https://github.com/talwrii/pop-note/issues"
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
pop-note = "pop_note.main:main"
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["setuptools>=61"]
|
|
29
|
+
build-backend = "setuptools.build_meta"
|
pop_note-0.1.0/setup.cfg
ADDED