kimal3 0.9.2 → 0.9.3
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.
- package/coach/de/kimal3-coach.md +1 -1
- package/coach/en/kimal3-coach.md +1 -1
- package/dist/befehle/zeigen-einrichten.js +29 -9
- package/dist/befehle/zeigen-pruefen.js +56 -0
- package/dist/befehle/zeigen-start.js +8 -10
- package/dist/befehle/zeigen.js +20 -8
- package/dist/befehle-katalog-en.js +4 -4
- package/dist/befehle-katalog.js +2 -2
- package/dist/befehlswoerter.js +4 -0
- package/dist/kimal3.js +2 -0
- package/dist/texte/zeigen.js +54 -2
- package/dist/zeigen-freigaben.js +65 -0
- package/dist/zeigen-rechnung.js +72 -19
- package/dist/zeigen-umgebung.js +9 -13
- package/package.json +1 -1
- package/skill/commands/k3zeigen.md +154 -8
- package/skill/en/commands/k3show.md +150 -8
- package/zeigen/gemeinsam.py +86 -4
- package/zeigen/starter.py +24 -8
- package/zeigen/werkzeug.py +17 -9
- package/zeigen/zeitleiste.py +59 -16
package/zeigen/zeitleiste.py
CHANGED
|
@@ -43,6 +43,17 @@ UMSCHALTER = {"ctrl": "Strg", "ctrl_l": "Strg", "ctrl_r": "Strg", "alt": "Alt",
|
|
|
43
43
|
"cmd": "Win", "cmd_l": "Win", "cmd_r": "Win", "shift": "Umschalt", "shift_l": "Umschalt",
|
|
44
44
|
"shift_r": "Umschalt", "alt_gr": "AltGr"}
|
|
45
45
|
KOMBI = ("Strg", "Alt", "Win")
|
|
46
|
+
# Mac (Aufnehmer macOS, input_observer/darwin.py `_KEY_NAMES`): In `key_name` -- der Spalte, die wir lesen -- stehen die
|
|
47
|
+
# ROHEN Namen mit Seite (cmd_l/cmd_r, alt_l/alt_r, shift_l/shift_r, ctrl_l/ctrl_r); vereinheitlicht ist nur
|
|
48
|
+
# `canonical_key_name`. Beide Formen gelten, damit auch eine Aufnahme mit vereinheitlichten Namen stimmt.
|
|
49
|
+
# `fn` und `caps_lock` kommen als eigene Tasten und sind weder Text noch Kombination.
|
|
50
|
+
MAC_UMSCHALTER = {name + seite: zeichen for name, zeichen in {"cmd": "⌘", "alt": "⌥", "ctrl": "⌃", "shift": "⇧"}.items()
|
|
51
|
+
for seite in ("", "_l", "_r")}
|
|
52
|
+
MAC_REIHENFOLGE = ("⌃", "⌥", "⇧", "⌘") # wie das Mac-Menue: Ctrl, Option, Umschalt, Befehl
|
|
53
|
+
MAC_KOMBI = ("⌃", "⌥", "⌘") # nur diese machen eine Kombination; Umschalt allein ist Grossschreibung
|
|
54
|
+
MAC_STUMM = ("fn", "caps_lock")
|
|
55
|
+
MAC_PFEILE = {"up": "↑", "down": "↓", "left": "←", "right": "→"}
|
|
56
|
+
MAC_EINGABEFELDER = ("edit", "axtextfield", "axtextarea", "axcombobox", "axsearchfield")
|
|
46
57
|
ZEICHEN = {"space": " ", "enter": "⏎", "tab": "⇥", "up": "↑", "down": "↓", "left": "←", "right": "→"}
|
|
47
58
|
# Passwortfelder: Windows meldet `IsPassword`, gespeichert wird das Merkmal aber nicht (StructuralElement kennt kein
|
|
48
59
|
# Feld dafuer, structural.py). Deshalb wie der Aufnehmer selbst (shadow/events.py native_field_is_secure) zusaetzlich
|
|
@@ -66,7 +77,7 @@ def _woerter_teilen(text: str) -> str:
|
|
|
66
77
|
return re.sub(r"(?<=[a-z0-9])(?=[A-Z])", " ", re.sub(r"[-_.#]+", " ", text))
|
|
67
78
|
|
|
68
79
|
|
|
69
|
-
def passwortfeld(beobachtung: dict | None) -> bool:
|
|
80
|
+
def passwortfeld(beobachtung: dict | None, plattform: str = "win32") -> bool:
|
|
70
81
|
if not isinstance(beobachtung, dict):
|
|
71
82
|
return False
|
|
72
83
|
e = beobachtung.get("element") or {}
|
|
@@ -76,7 +87,8 @@ def passwortfeld(beobachtung: dict | None) -> bool:
|
|
|
76
87
|
if "password" in rollen or "secure" in rollen:
|
|
77
88
|
return True
|
|
78
89
|
namen = [e.get("automation_id"), e.get("class_name")]
|
|
79
|
-
if
|
|
90
|
+
felder = MAC_EINGABEFELDER if plattform == "darwin" else ("edit",)
|
|
91
|
+
if str(e.get("control_type") or e.get("role") or "").casefold() in felder:
|
|
80
92
|
namen.append(e.get("name"))
|
|
81
93
|
return any(isinstance(n, str) and GEHEIM.search(_woerter_teilen(n)) for n in namen)
|
|
82
94
|
|
|
@@ -86,11 +98,14 @@ def _programm(beobachtung: dict) -> str | None:
|
|
|
86
98
|
return re.sub(r"\.exe$", "", name, flags=re.IGNORECASE).casefold() or None
|
|
87
99
|
|
|
88
100
|
|
|
89
|
-
def _klick_text(beobachtung: dict | None, t: dict) -> str:
|
|
101
|
+
def _klick_text(beobachtung: dict | None, t: dict, plattform: str = "win32") -> str:
|
|
90
102
|
if not isinstance(beobachtung, dict) or not beobachtung.get("element"):
|
|
91
103
|
return t["unbekannt"]
|
|
92
104
|
e = beobachtung["element"]
|
|
93
|
-
|
|
105
|
+
rolle = _eine_zeile(e.get("role") or e.get("control_type"))
|
|
106
|
+
if plattform == "darwin":
|
|
107
|
+
rolle = re.sub(r"^AX(?=[A-Z])", "", rolle) # AXLink -> Link
|
|
108
|
+
teile = [rolle]
|
|
94
109
|
if _eine_zeile(e.get("name")):
|
|
95
110
|
teile.append(f"{t['auf']}{_eine_zeile(e['name'])}{t['zu']}")
|
|
96
111
|
programm = _programm(beobachtung)
|
|
@@ -118,7 +133,9 @@ def _saetze(woerter: list) -> list:
|
|
|
118
133
|
return saetze + ([satz] if satz else [])
|
|
119
134
|
|
|
120
135
|
|
|
121
|
-
def _kombi_name(e: dict) -> str:
|
|
136
|
+
def _kombi_name(e: dict, plattform: str = "win32") -> str:
|
|
137
|
+
if plattform == "darwin" and e.get("taste") in MAC_PFEILE:
|
|
138
|
+
return MAC_PFEILE[e["taste"]]
|
|
122
139
|
zeichen = e.get("zeichen") or ""
|
|
123
140
|
if len(zeichen) == 1 and 1 <= ord(zeichen) <= 26: # Strg+Buchstabe kommt als Steuerzeichen: \x03 = Strg+C
|
|
124
141
|
return chr(64 + ord(zeichen))
|
|
@@ -127,11 +144,15 @@ def _kombi_name(e: dict) -> str:
|
|
|
127
144
|
return (e.get("taste") or "?").replace("_", " ").title()
|
|
128
145
|
|
|
129
146
|
|
|
130
|
-
def zeitleiste_aus(kopf: dict, woerter: list, ereignisse: list, sprache: str, ohne_getipptes: bool
|
|
147
|
+
def zeitleiste_aus(kopf: dict, woerter: list, ereignisse: list, sprache: str, ohne_getipptes: bool,
|
|
148
|
+
plattform: str = "win32") -> tuple:
|
|
131
149
|
"""Text der Zeitleiste und ihre Zahlen. kopf: aufgabe, datum (fertiger Text), dauer_sek. woerter: (Anfang, Ende, Wort)
|
|
132
150
|
in Sekunden ab Aufnahmestart. ereignisse: dicts mit t, art (click|press|release|…), beobachtung, bei Tasten
|
|
133
|
-
taste und zeichen."""
|
|
151
|
+
taste und zeichen. plattform: `recording.platform` der Aufnahme, "darwin" = Mac, sonst wie Windows."""
|
|
134
152
|
t = TEXTE[sprache]
|
|
153
|
+
mac = plattform == "darwin"
|
|
154
|
+
umschalter = MAC_UMSCHALTER if mac else UMSCHALTER
|
|
155
|
+
kombi_reihe = MAC_KOMBI if mac else KOMBI
|
|
135
156
|
zahlen = {"saetze": 0, "klicks": 0, "tippstellen": 0, "passwortfelder": 0}
|
|
136
157
|
zeilen = [] # (Sekunde, Verb, Text)
|
|
137
158
|
|
|
@@ -161,27 +182,36 @@ def zeitleiste_aus(kopf: dict, woerter: list, ereignisse: list, sprache: str, oh
|
|
|
161
182
|
if art == "click":
|
|
162
183
|
folge_ende()
|
|
163
184
|
gehalten.clear()
|
|
164
|
-
klick_geheim = passwortfeld(e.get("beobachtung"))
|
|
165
|
-
zeilen.append((e["t"], t["klickt"], _klick_text(e.get("beobachtung"), t)))
|
|
185
|
+
klick_geheim = passwortfeld(e.get("beobachtung"), plattform)
|
|
186
|
+
zeilen.append((e["t"], t["klickt"], _klick_text(e.get("beobachtung"), t, plattform)))
|
|
166
187
|
zahlen["klicks"] += 1
|
|
167
188
|
continue
|
|
168
189
|
if art == "release":
|
|
169
|
-
gehalten.discard(
|
|
190
|
+
gehalten.discard(umschalter.get(taste))
|
|
170
191
|
continue
|
|
171
192
|
if art != "press":
|
|
172
193
|
continue # Mausbewegung, Scrollen
|
|
173
|
-
if taste in
|
|
174
|
-
|
|
194
|
+
if mac and taste in MAC_STUMM:
|
|
195
|
+
continue
|
|
196
|
+
if taste in umschalter:
|
|
197
|
+
gehalten.add(umschalter[taste])
|
|
175
198
|
continue
|
|
176
199
|
zeichen = e.get("zeichen") or ""
|
|
177
200
|
steuer = taste is None and len(zeichen) == 1 and 1 <= ord(zeichen) <= 26 # Tab/Enter haben einen Namen
|
|
178
201
|
# Windows meldet AltGr als ctrl_l + alt_r, also Strg+Alt: ein druckbares Zeichen dabei ist Text („@", „€").
|
|
179
202
|
altgr = bool(zeichen) and zeichen.isprintable() and ("AltGr" in gehalten or {"Strg", "Alt"} <= gehalten)
|
|
203
|
+
if mac: # Option (auch mit Umschalt) + druckbares Zeichen ist Text („@" auf Option+L); mit Befehl oder Ctrl nicht
|
|
204
|
+
altgr = bool(zeichen) and zeichen.isprintable() and "⌥" in gehalten and not {"⌘", "⌃"} & gehalten
|
|
180
205
|
# Im Passwortfeld wird keine Kombination eine eigene Zeile: sie verriete Tasten und teilte die Ausblendung.
|
|
181
|
-
geheim = klick_geheim or (folge is not None and folge["geheim"]) or passwortfeld(e.get("beobachtung"))
|
|
182
|
-
kombi = [m for m in
|
|
206
|
+
geheim = klick_geheim or (folge is not None and folge["geheim"]) or passwortfeld(e.get("beobachtung"), plattform)
|
|
207
|
+
kombi = [m for m in kombi_reihe if m in gehalten] or ([("⌃" if mac else "Strg")] if steuer else [])
|
|
183
208
|
if kombi and not altgr and not geheim:
|
|
184
209
|
folge_ende()
|
|
210
|
+
if mac: # Mac-Schreibweise ohne Plus: [⌘C], [⌥→]; Umschalt kommt nur dazu, wenn schon eine Kombination da ist
|
|
211
|
+
kombi = [m for m in MAC_REIHENFOLGE if m in kombi or (m == "⇧" and m in gehalten)]
|
|
212
|
+
zeilen.append((e["t"], t["tippt"], "[" + "".join(kombi) + _kombi_name(e, plattform) + "]"))
|
|
213
|
+
gehalten.clear()
|
|
214
|
+
continue
|
|
185
215
|
mods = kombi + (["Umschalt"] if "Umschalt" in gehalten else [])
|
|
186
216
|
zeilen.append((e["t"], t["tippt"], "[" + "+".join(t.get(m, m) for m in mods) + f"+{_kombi_name(e)}]"))
|
|
187
217
|
gehalten.clear() # eine Kombination verbraucht die Umschalter; die naechste Taste kommt wieder mit eigenen
|
|
@@ -192,7 +222,7 @@ def zeitleiste_aus(kopf: dict, woerter: list, ereignisse: list, sprache: str, oh
|
|
|
192
222
|
if folge is None:
|
|
193
223
|
folge = {"t": e["t"], "text": "", "geheim": klick_geheim, "zuletzt": e["t"]}
|
|
194
224
|
folge["zuletzt"] = e["t"]
|
|
195
|
-
folge["geheim"] = folge["geheim"] or passwortfeld(e.get("beobachtung"))
|
|
225
|
+
folge["geheim"] = folge["geheim"] or passwortfeld(e.get("beobachtung"), plattform)
|
|
196
226
|
if taste == "backspace":
|
|
197
227
|
folge["text"] = folge["text"][:-1]
|
|
198
228
|
elif zeichen and zeichen.isprintable():
|
|
@@ -209,6 +239,18 @@ def zeitleiste_aus(kopf: dict, woerter: list, ereignisse: list, sprache: str, oh
|
|
|
209
239
|
|
|
210
240
|
# --- Aufnahme lesen, Ton in Woerter ------------------------------------------------------------------------------
|
|
211
241
|
|
|
242
|
+
def _lies_plattform(ordner: str) -> str:
|
|
243
|
+
"""`recording.platform` ("darwin" auf dem Mac). Aeltere Aufnahmen ohne Spalte oder mit leerem Wert: "win32"."""
|
|
244
|
+
db = sqlite3.connect(f"file:{os.path.join(ordner, ROH, 'recording.db')}?mode=ro", uri=True)
|
|
245
|
+
try:
|
|
246
|
+
wert = db.execute("select platform from recording").fetchone()
|
|
247
|
+
except sqlite3.OperationalError: # keine Spalte
|
|
248
|
+
return "win32"
|
|
249
|
+
finally:
|
|
250
|
+
db.close()
|
|
251
|
+
return wert[0] if wert and wert[0] else "win32"
|
|
252
|
+
|
|
253
|
+
|
|
212
254
|
def _lies_aufnahme(ordner: str):
|
|
213
255
|
db = sqlite3.connect(f"file:{os.path.join(ordner, ROH, 'recording.db')}?mode=ro", uri=True)
|
|
214
256
|
try:
|
|
@@ -307,6 +349,7 @@ def auswerten(ordner: str, sprache: str, ohne_getipptes: bool, modell_da) -> dic
|
|
|
307
349
|
return dict(daten, grund="keine-aufnahme")
|
|
308
350
|
try:
|
|
309
351
|
start, beschreibung, ereignisse, letzte, ton = _lies_aufnahme(ordner)
|
|
352
|
+
plattform = _lies_plattform(ordner)
|
|
310
353
|
except (sqlite3.Error, TypeError, ValueError):
|
|
311
354
|
return dict(daten, grund="aufnahme-unlesbar")
|
|
312
355
|
daten["dauer_sek"] = round(max(letzte, 0.0), 2)
|
|
@@ -321,7 +364,7 @@ def auswerten(ordner: str, sprache: str, ohne_getipptes: bool, modell_da) -> dic
|
|
|
321
364
|
dauer = max(letzte, ton_ende)
|
|
322
365
|
kopf = {"aufgabe": _aufgabe(ordner, beschreibung), "datum": time.strftime("%Y-%m-%d %H:%M", time.localtime(start)),
|
|
323
366
|
"dauer_sek": dauer}
|
|
324
|
-
text, zahlen = zeitleiste_aus(kopf, woerter, ereignisse, sprache, ohne_getipptes)
|
|
367
|
+
text, zahlen = zeitleiste_aus(kopf, woerter, ereignisse, sprache, ohne_getipptes, plattform)
|
|
325
368
|
daten.update(zahlen, dauer_sek=round(dauer, 2))
|
|
326
369
|
if zahlen["saetze"] == 0:
|
|
327
370
|
return dict(daten, ergebnis="keine-sprache")
|