init commit

This commit is contained in:
unknown
2025-08-19 08:06:37 -04:00
commit 2957b5515a
743 changed files with 45495 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
# Subtitles
Custom subtitles with settings to tweak color, position and timeout.
Talon's default subtitles needs to be disabled from the Talon menu to avoid duplicates.
![Subtitle](./images/subtitle.png)
## Show / hide subtitles
### Show subtitles
Setting: `user.subtitles_show = true`
### Hide subtitles
Setting: `user.subtitles_show = false`
## Other options
See additional configuration options documented in `subtitles.talon`.
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+29
View File
@@ -0,0 +1,29 @@
from talon import actions, speech_system
from talon.grammar import Phrase
from .subtitles import show_subtitle
def on_pre_phrase(phrase: Phrase):
if skip_phrase(phrase):
return
words = phrase["phrase"]
current_phrase = " ".join(words)
show_subtitle(current_phrase)
def skip_phrase(phrase: Phrase) -> bool:
return not phrase.get("phrase") or skip_phrase_in_sleep(phrase)
def skip_phrase_in_sleep(phrase: Phrase) -> bool:
"""Returns true if the rule is <phrase> in sleep mode"""
return (
not actions.speech.enabled()
and len(phrase["parsed"]) == 1
and phrase["parsed"][0]._name == "___ltphrase_gt__"
)
speech_system.register("phrase", on_pre_phrase)
+151
View File
@@ -0,0 +1,151 @@
from typing import Any, Callable, Optional, Sequence, Type
from talon import Module, app, cron, ctrl, settings, ui
from talon.canvas import Canvas
from talon.skia.canvas import Canvas as SkiaCanvas
from talon.skia.imagefilter import ImageFilter
from talon.types import Rect
mod = Module()
def setting(
name: str, type: Type, desc: str, *, default: Optional[Any] = None
) -> Callable[[], type]:
mod.setting(f"subtitles_{name}", type, default=default, desc=f"Subtitles: {desc}")
return lambda: settings.get(f"user.subtitles_{name}")
setting_show = setting(
"show",
bool,
"If true show (custom) subtitles",
default=False,
)
setting_screens = setting(
"screens",
str,
"Show on which screens: 'all', 'main', 'cursor', 'focus'",
)
setting_size = setting(
"size",
int,
"Subtitle size in pixels",
)
setting_color = setting(
"color",
str,
"Subtitle color",
)
setting_color_outline = setting(
"color_outline",
str,
"Subtitle outline color",
)
setting_timeout_per_char = setting(
"timeout_per_char",
int,
"For each character in the subtitle extend the timeout by this amount in ms",
)
setting_timeout_min = setting(
"timeout_min",
int,
"Minimum time for a subtitle to show in ms",
)
setting_timeout_max = setting(
"timeout_max",
int,
"Maximum time for a subtitle to show in ms",
)
setting_y = setting(
"y",
float,
"Percentage of screen hight to show subtitle at. 0=top, 1=bottom",
)
mod = Module()
canvases: list[Canvas] = []
def show_subtitle(text: str):
"""Show subtitle"""
if not setting_show():
return
clear_canvases()
screens = get_screens()
for screen in screens:
canvas = show_text_on_screen(screen, text)
canvases.append(canvas)
def get_screens() -> Sequence[ui.Screen]:
screen = setting_screens()
match screen:
case "main":
return [ui.main_screen()]
case "all":
return ui.screens()
case "cursor":
x, y = ctrl.mouse_pos()
return [ui.screen_containing(x, y)]
case "focus":
return [ui.active_window().screen]
case _:
raise ValueError(f"Unknown screen setting: {screen}")
def show_text_on_screen(screen: ui.Screen, text: str):
timeout = calculate_timeout(text)
canvas = Canvas.from_screen(screen)
canvas.register("draw", lambda c: on_draw(c, screen, text))
canvas.freeze()
cron.after(f"{timeout}ms", canvas.close)
return canvas
def on_draw(c: SkiaCanvas, screen: ui.Screen, text: str):
scale = screen.scale if app.platform != "mac" else 1
size = setting_size() * scale
rect = set_text_size_and_get_rect(c, size, text)
x = c.rect.center.x - rect.center.x
# Clamp coordinate to make sure entire text is visible
y = max(
min(
c.rect.y + setting_y() * c.rect.height + c.paint.textsize / 2,
c.rect.bot - rect.bot,
),
c.rect.top - rect.top,
)
c.paint.imagefilter = ImageFilter.drop_shadow(2, 2, 1, 1, "000000")
c.paint.style = c.paint.Style.FILL
c.paint.color = setting_color()
c.draw_text(text, x, y)
# Outline
c.paint.imagefilter = None
c.paint.style = c.paint.Style.STROKE
c.paint.color = setting_color_outline()
c.draw_text(text, x, y)
def calculate_timeout(text: str) -> int:
ms_per_char = setting_timeout_per_char()
ms_min = setting_timeout_min()
ms_max = setting_timeout_max()
return min(ms_max, max(ms_min, len(text) * ms_per_char))
def set_text_size_and_get_rect(c: SkiaCanvas, size: int, text: str) -> Rect:
while True:
c.paint.textsize = size
rect = c.paint.measure_text(text)[1]
if rect.width < c.width * 0.8:
return rect
size *= 0.9
def clear_canvases():
for canvas in canvases:
canvas.close()
canvases.clear()
@@ -0,0 +1,23 @@
settings():
# Show subtitles?
user.subtitles_show = false
# Screens on which to show subtitles:
# "all" - all screens
# "main" - main screen as configured in OS
# "cursor" - screen containing mouse pointer
# "focus" - screen containing active/focused window/app
user.subtitles_screens = "main"
# 100 px maximum subtitle font size
user.subtitles_size = 100
# White subtitle color
user.subtitles_color = "ffffff"
# Slightly dark subtitle outline
user.subtitles_color_outline = "aaaaaa"
# For each character in the subtitle, extend the timeout 50 ms
user.subtitles_timeout_per_char = 50
# 750 ms is the minimum time to display a subtitle
user.subtitles_timeout_min = 750
# 3 seconds is the maximum time to display a subtitle
user.subtitles_timeout_max = 3000
# Position subtitles at the bottom of the screen (93% from top)
user.subtitles_y = 0.93