mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-02 19:32:43 +00:00
feat(config): add kitty pre‑shutdown session save and config updates
This commit is contained in:
1 parent
cae4fb8766
commit
7e2ae53560
6 files changed
+84
-45
No files matched your search
@@ -8,31 +8,34 @@ One file, three entry points (kitty runs it under a different __name__ each):
|
||||
startup_session at it whenever no running kitty owns today's session (an
|
||||
flock held for the owner's lifetime). Every launch after kitty was closed
|
||||
reopens the day; a second concurrent instance starts plain instead of
|
||||
duplicating today's tabs.
|
||||
duplicating today's tabs. `kitty --session <past day>` (the kitty-days tv
|
||||
cable) opens that day as a read-only snapshot instead.
|
||||
- watcher (__kitty_watcher__): SAVE_DELAY seconds after a window opens,
|
||||
closes, moves, gains focus or finishes a command (and every SAVE_EVERY
|
||||
seconds, for output from long-running programs), rewrites the session this
|
||||
instance started with: tabs/splits/cwds, each shell window's scrollback, and
|
||||
the program running in it. On restore the shell `cat`s the scrollback, then
|
||||
re-runs the program. Days opened later via the picker are left as read-only
|
||||
snapshots.
|
||||
- kitten (map ... kitten day_session.py): picker over every archived day,
|
||||
newest first; the choice is opened with goto_session.
|
||||
re-runs the program.
|
||||
- kitten `day_session.py flush` (sent over remote control by kitty-day-save
|
||||
before shutdown/reboot): saves right away, then holds further saves for
|
||||
FREEZE seconds so windows dying during shutdown can't shrink the saved day.
|
||||
"""
|
||||
import fcntl
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import date, timedelta
|
||||
from time import monotonic
|
||||
|
||||
from kittens.tui.handler import result_handler
|
||||
|
||||
ROOT = os.path.expanduser('~/.local/share/kitty-sessions')
|
||||
SAVE_DELAY = 1 # seconds; batches bursts of events, and the most change lost when kitty dies
|
||||
SAVE_EVERY = 30 # seconds; catches output that arrives without any window event
|
||||
FREEZE = 60 # seconds without saves after a flush; long enough for any shutdown, short if it's cancelled
|
||||
SB_VAR = 'day_scrollback' # user var naming a window's scrollback dump; kitty saves/restores it via launch --var
|
||||
UNSERIALIZE = 'kitty-unserialize-data=' # kitty.constants.unserialize_launch_flag
|
||||
# Never replay a command that was mid-run when the session was saved if any word
|
||||
@@ -149,12 +152,14 @@ def on_load(boss, data) -> None:
|
||||
args[1] = UNSERIALIZE + json.dumps(data)
|
||||
return shlex.join(args)
|
||||
|
||||
frozen_until = 0.0
|
||||
|
||||
def save(timer_id: int) -> None:
|
||||
nonlocal last, pending
|
||||
pending = False
|
||||
windows = [w for w in boss.all_windows if w.created_in_session_name == name]
|
||||
# Nothing left in the session means kitty is closing: keep the last good save.
|
||||
if not windows:
|
||||
if not windows or monotonic() < frozen_until:
|
||||
return
|
||||
os.makedirs(sb_dir, exist_ok=True)
|
||||
keep = set()
|
||||
@@ -185,7 +190,14 @@ def on_load(boss, data) -> None:
|
||||
pending = True
|
||||
add_timer(save, SAVE_DELAY, False)
|
||||
|
||||
def flush() -> None:
|
||||
nonlocal frozen_until
|
||||
frozen_until = 0.0
|
||||
save(0)
|
||||
frozen_until = monotonic() + FREEZE
|
||||
|
||||
_schedule_save = schedule
|
||||
boss.day_session_flush = flush # the flush kitten runs in its own module copy, so it finds this via boss
|
||||
add_timer(lambda timer_id: schedule(), SAVE_EVERY, True)
|
||||
|
||||
|
||||
@@ -206,23 +218,26 @@ def main(args: list[str]) -> None:
|
||||
|
||||
@result_handler(no_ui=True)
|
||||
def handle_result(args: list[str], answer: str, target_window_id: int, boss) -> None:
|
||||
from kitty.session import goto_session
|
||||
if 'flush' in args and (flush := getattr(boss, 'day_session_flush', None)):
|
||||
flush()
|
||||
|
||||
days = sorted(glob.glob(os.path.join(ROOT, '*', '*.kitty-session')), key=os.path.basename, reverse=True)
|
||||
if not days:
|
||||
boss.show_error('No day sessions', f'Nothing archived under {ROOT} yet.')
|
||||
return
|
||||
|
||||
def label(path: str) -> str:
|
||||
d = date.fromisoformat(os.path.basename(path).partition('.')[0])
|
||||
return f'{d} {d:%a} week of {os.path.basename(os.path.dirname(path))}'
|
||||
|
||||
boss.choose_entry('Open a day', ((p, label(p)) for p in days), lambda p: p and goto_session(boss, (p,)))
|
||||
def session_arg() -> str | None:
|
||||
"""`--session` from kitty's own command line (geninclude runs inside kitty)."""
|
||||
argv = sys.argv[1:]
|
||||
for i, a in enumerate(argv):
|
||||
if a.startswith('--session='):
|
||||
return a.partition('=')[2]
|
||||
if a == '--session' and i + 1 < len(argv):
|
||||
return argv[i + 1]
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
path = today_path()
|
||||
if own_day(path):
|
||||
other = session_arg()
|
||||
# An explicit --session for another day must not claim today's lock.
|
||||
if (other is None or os.path.abspath(os.path.expanduser(other)) == path) and own_day(path):
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
try:
|
||||
open(path, 'x').close() # empty session = one tab with the default shell
|
||||
|
||||
Reference in new issue
Block a user