diff --git a/.config/ghostty/config##Default b/.config/ghostty/config##Default index b927875..a45f478 100644 --- a/.config/ghostty/config##Default +++ b/.config/ghostty/config##Default @@ -22,3 +22,4 @@ keybind = all:ctrl+enter=unbind keybind = all:ctrl+shift+j=next_tab keybind = all:ctrl+shift+k=last_tab keybind = all:ctrl+grave_accent=toggle_quick_terminal +keybind = shift+enter=text:\x1b\r diff --git a/.config/hypr/keybindings.conf b/.config/hypr/keybindings.conf index 194a87e..ed6e6e7 100644 --- a/.config/hypr/keybindings.conf +++ b/.config/hypr/keybindings.conf @@ -96,8 +96,9 @@ bind = $mainMod, w, exec, rofi -show window -theme $HOME/.config/rofi/launchers/ bind = $mainMod SHIFT, w, exec, "$HOME/.config/rofi/scripts/rofi-wallpaper.sh" bind = $mainMod SHIFT, d, exec, "$HOME/.config/rofi/scripts/rofi-docs.sh" bind = SUPER, j, exec, "$HOME/.config/rofi/scripts/rofi-jellyfin-dir.sh" -bind = $mainMod SHIFT, t, exec, "$HOME/.config/rofi/scripts/rofi-launch-texthooker-steam.sh" -bind = $mainMod, t, exec, "$HOME/projects/scripts/popup-ai-translator.py" +bind = SUPER, t, exec, "$HOME/.config/rofi/scripts/rofi-launch-texthooker-steam.sh" +bind = $mainMod SHIFT, t, exec, "$HOME/projects/scripts/popup-ai-translator.py" +bind = SUPER SHIFT, g, exec, "$HOME/.config/rofi/scripts/rofi-vn-helper.sh" # ncmcppp bind = $mainMod, n, exec, uwsm app -sb -- ghostty --command=/usr/bin/ncmpcpp diff --git a/.config/mpv/script-opts/subs2srs.conf b/.config/mpv/script-opts/subs2srs.conf index db01b5a..ce529e4 100644 --- a/.config/mpv/script-opts/subs2srs.conf +++ b/.config/mpv/script-opts/subs2srs.conf @@ -14,6 +14,7 @@ deck_name=Minecraft # If you don't have a model for Japanese, get it from # https://tatsumoto.neocities.org/blog/setting-up-anki.html#import-an-example-mining-deck model_name=Lapis +# model_name=Kiku # Field names as they appear in the selected note type. # If you set `audio_field` or `image_field` empty, diff --git a/projects/scripts/popup-ai-translator.py b/projects/scripts/popup-ai-translator.py index 3b27ea1..2870323 100755 --- a/projects/scripts/popup-ai-translator.py +++ b/projects/scripts/popup-ai-translator.py @@ -22,7 +22,7 @@ if not OPENROUTER_API_KEY: with open(key_file, "r") as f: OPENROUTER_API_KEY = f.read().strip() -SYSTEM_PROMPT = """You are my Japanese-learning assistant. Help me acquire Japanese through deep, AJATT-aligned analysis. +TRANSLATION_PROMPT = """You are my Japanese-learning assistant. Help me acquire Japanese through deep, AJATT-aligned analysis. For every input, output exactly using clean plain text formatting: @@ -69,6 +69,24 @@ Optional Additions (only when valuable): Goal: Deep comprehension, natural grammar internalization, nuanced vocabulary, progress toward Japanese-only understanding.""" +GRAMMAR_PROMPT = """ + You are a Japanese grammar analysis assistant. + + The user will provide Japanese sentences. Your task is to explain the **grammar and structure in English**, prioritizing how the sentence is constructed rather than translating word-for-word. + + Rules: + + * Always show the original Japanese first. + * Provide a short, natural English gloss only if helpful. + * Explain grammar patterns, verb forms, particles, and omissions. + * Emphasize nuance, implication, and speaker intent. + * Avoid unnecessary vocabulary definitions unless they affect the grammar. + * Assume the user is actively studying Japanese and wants deep understanding. + + Your explanations should help the user internalize patterns, not memorize translations. + +""" + def show_error(message: str) -> None: """Display an error dialog using zenity.""" @@ -132,6 +150,29 @@ def get_input() -> str | None: return result.stdout.strip() +def get_mode() -> str | None: + """Ask user to choose between translation and grammar assistant.""" + result = subprocess.run( + [ + "zenity", + "--list", + "--title", + "Japanese Assistant", + "--text", + "Choose analysis mode:", + "--column=Mode", + "Translation (full analysis)", + "Grammar (structure focus)", + ], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + ) + if result.returncode != 0: + return None + return result.stdout.strip() + + def show_notification(message: str, body: str) -> subprocess.Popen: """Show a notification using notify-send.""" return subprocess.Popen( @@ -169,7 +210,7 @@ def display_result(content: str) -> None: ) -def make_api_request(user_input: str) -> dict: +def make_api_request(user_input: str, system_prompt: str) -> dict: """Make the API request to OpenRouter.""" headers = { "Content-Type": "application/json", @@ -181,7 +222,7 @@ def make_api_request(user_input: str) -> dict: payload = { "model": MODEL, "messages": [ - {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "system", "content": system_prompt}, {"role": "user", "content": user_input}, ], "temperature": 0.7, @@ -197,6 +238,17 @@ def main() -> int: show_error("OPENROUTER_API_KEY environment variable is not set.") return 1 + # Ask user for mode + mode = get_mode() + if not mode: + return 0 + + # Select appropriate prompt + if "Grammar" in mode: + system_prompt = GRAMMAR_PROMPT + else: + system_prompt = TRANSLATION_PROMPT + # Get input from user user_input = get_input() if not user_input: @@ -207,7 +259,7 @@ def main() -> int: try: # Make API request - response = make_api_request(user_input) + response = make_api_request(user_input, system_prompt) # Close loading notification close_notification()