KeyboardPaster 0.1.16__tar.gz → 0.1.18__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.
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/PKG-INFO +8 -3
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/PKG-INFO +8 -3
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/README.md +7 -2
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/keyboardpaster/app.py +189 -34
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/keyboardpaster/keyboardpaster_app.kv +49 -12
- keyboardpaster-0.1.18/keyboardpaster/shared_resources.py +1 -0
- keyboardpaster-0.1.16/keyboardpaster/shared_resources.py +0 -1
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/SOURCES.txt +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/dependency_links.txt +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/entry_points.txt +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/requires.txt +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/top_level.txt +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/LICENSE +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/keyboardpaster/__init__.py +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/keyboardpaster/keyboard_layout_detector.py +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/keyboardpaster/modules/__init__.py +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/keyboardpaster/modules/autoupdate.py +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/setup.cfg +0 -0
- {keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: KeyboardPaster
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.18
|
|
4
4
|
Summary: A Python package to type text using the keyboard module
|
|
5
5
|
Home-page: https://github.com/KnutssonDevelopment/KeyboardPaster
|
|
6
6
|
Author: Brian Knutsson
|
|
@@ -37,10 +37,15 @@ A Python package to type text using the keyboard module. This is useful in situa
|
|
|
37
37
|
|
|
38
38
|
## Future features
|
|
39
39
|
- Paste images as base64 code
|
|
40
|
-
- Enter would activate the Paste operation
|
|
41
|
-
- Shift+Enter would move all inputs downwards and put the cursor into the next, now empty, field
|
|
42
40
|
|
|
43
41
|
## Changes
|
|
42
|
+
### v0.1.18
|
|
43
|
+
- FEATURE: Added a possibility to have multiple profiles.
|
|
44
|
+
|
|
45
|
+
### v0.1.17
|
|
46
|
+
- FEATURE: Added a button to put text into clipboard
|
|
47
|
+
- UPDATE: Changed the keyboard paste option into an icon
|
|
48
|
+
|
|
44
49
|
### v0.1.16
|
|
45
50
|
- FEATURE: Changes the launcher to run as a gui application and not show the command line when executed
|
|
46
51
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: KeyboardPaster
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.18
|
|
4
4
|
Summary: A Python package to type text using the keyboard module
|
|
5
5
|
Home-page: https://github.com/KnutssonDevelopment/KeyboardPaster
|
|
6
6
|
Author: Brian Knutsson
|
|
@@ -37,10 +37,15 @@ A Python package to type text using the keyboard module. This is useful in situa
|
|
|
37
37
|
|
|
38
38
|
## Future features
|
|
39
39
|
- Paste images as base64 code
|
|
40
|
-
- Enter would activate the Paste operation
|
|
41
|
-
- Shift+Enter would move all inputs downwards and put the cursor into the next, now empty, field
|
|
42
40
|
|
|
43
41
|
## Changes
|
|
42
|
+
### v0.1.18
|
|
43
|
+
- FEATURE: Added a possibility to have multiple profiles.
|
|
44
|
+
|
|
45
|
+
### v0.1.17
|
|
46
|
+
- FEATURE: Added a button to put text into clipboard
|
|
47
|
+
- UPDATE: Changed the keyboard paste option into an icon
|
|
48
|
+
|
|
44
49
|
### v0.1.16
|
|
45
50
|
- FEATURE: Changes the launcher to run as a gui application and not show the command line when executed
|
|
46
51
|
|
|
@@ -8,10 +8,15 @@ A Python package to type text using the keyboard module. This is useful in situa
|
|
|
8
8
|
|
|
9
9
|
## Future features
|
|
10
10
|
- Paste images as base64 code
|
|
11
|
-
- Enter would activate the Paste operation
|
|
12
|
-
- Shift+Enter would move all inputs downwards and put the cursor into the next, now empty, field
|
|
13
11
|
|
|
14
12
|
## Changes
|
|
13
|
+
### v0.1.18
|
|
14
|
+
- FEATURE: Added a possibility to have multiple profiles.
|
|
15
|
+
|
|
16
|
+
### v0.1.17
|
|
17
|
+
- FEATURE: Added a button to put text into clipboard
|
|
18
|
+
- UPDATE: Changed the keyboard paste option into an icon
|
|
19
|
+
|
|
15
20
|
### v0.1.16
|
|
16
21
|
- FEATURE: Changes the launcher to run as a gui application and not show the command line when executed
|
|
17
22
|
|
|
@@ -2,6 +2,8 @@ import time
|
|
|
2
2
|
import re
|
|
3
3
|
import json
|
|
4
4
|
import pkg_resources
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
5
7
|
from pynput.keyboard import Controller, Key
|
|
6
8
|
|
|
7
9
|
from kivymd.app import MDApp
|
|
@@ -11,6 +13,9 @@ from kivy.lang import Builder
|
|
|
11
13
|
from kivy.clock import Clock
|
|
12
14
|
from kivy.core.window import Window
|
|
13
15
|
from kivymd.uix.selectioncontrol.selectioncontrol import MDCheckbox
|
|
16
|
+
from kivymd.uix.button.button import MDRaisedButton
|
|
17
|
+
from kivymd.uix.dialog import MDDialog
|
|
18
|
+
from kivymd.uix.textfield import MDTextField
|
|
14
19
|
|
|
15
20
|
from keyboardpaster.keyboard_layout_detector import get_keyboard_layout
|
|
16
21
|
from keyboardpaster.modules.autoupdate import autoupdate
|
|
@@ -124,13 +129,21 @@ class KeyboardPasterApp(MDApp):
|
|
|
124
129
|
start_delay = ObjectProperty(None)
|
|
125
130
|
layout = 'EN_US'
|
|
126
131
|
|
|
132
|
+
def __init__(self):
|
|
133
|
+
super().__init__()
|
|
134
|
+
self.config = None
|
|
135
|
+
self.json_config = 'saved_inputs.json'
|
|
136
|
+
|
|
127
137
|
def build(self):
|
|
128
138
|
self.theme_cls.primary_palette = "DeepPurple" # Change to your desired primary color
|
|
129
139
|
self.theme_cls.accent_palette = "Amber" # Change to your desired accent color
|
|
130
140
|
self.theme_cls.theme_style = "Light" # Set the theme to either "Light" or "Dark"
|
|
131
141
|
|
|
142
|
+
self.read_config()
|
|
143
|
+
|
|
132
144
|
self.detect_keyboard_layout()
|
|
133
|
-
Clock.schedule_once(self.load_inputs, 1)
|
|
145
|
+
#Clock.schedule_once(self.load_inputs, 1)
|
|
146
|
+
Clock.schedule_once(self.set_profile, 1)
|
|
134
147
|
self.title = f"Keyboard Paster v{app_version}"
|
|
135
148
|
Window.size = (1000, 700)
|
|
136
149
|
|
|
@@ -138,49 +151,182 @@ class KeyboardPasterApp(MDApp):
|
|
|
138
151
|
return Builder.load_file(kv_file_path)
|
|
139
152
|
|
|
140
153
|
def on_stop(self):
|
|
141
|
-
# self.save_inputs()
|
|
142
154
|
pass
|
|
143
155
|
|
|
144
|
-
def
|
|
156
|
+
def read_config(self):
|
|
145
157
|
try:
|
|
146
|
-
with open(
|
|
147
|
-
|
|
158
|
+
with open(self.json_config, "r", encoding='utf-8') as file:
|
|
159
|
+
self.config = json.load(file)
|
|
160
|
+
except FileNotFoundError:
|
|
161
|
+
pass
|
|
162
|
+
except AttributeError:
|
|
163
|
+
pass
|
|
164
|
+
except json.JSONDecodeError:
|
|
165
|
+
pass
|
|
166
|
+
except ValueError:
|
|
167
|
+
pass
|
|
168
|
+
except KeyError:
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
if 'profiles' in self.config:
|
|
172
|
+
pass
|
|
173
|
+
elif 'input_text_0' in self.config:
|
|
174
|
+
values = self.config
|
|
175
|
+
self.config = dict()
|
|
176
|
+
self.config['profiles'] = dict()
|
|
177
|
+
self.config['profiles']['DEFAULT'] = values
|
|
178
|
+
else:
|
|
179
|
+
self.config = dict()
|
|
180
|
+
self.config['profiles'] = dict()
|
|
181
|
+
self.config['profiles']['DEFAULT'] = dict()
|
|
182
|
+
|
|
183
|
+
def write_config(self):
|
|
184
|
+
with open(self.json_config, "w", encoding='utf-8') as file:
|
|
185
|
+
json.dump(self.config, file)
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def close_dialog(button_instance):
|
|
189
|
+
# Navigate up the widget tree until we find an MDDialog instance
|
|
190
|
+
current_widget = button_instance
|
|
191
|
+
while current_widget and not isinstance(current_widget, MDDialog):
|
|
192
|
+
current_widget = current_widget.parent
|
|
193
|
+
|
|
194
|
+
# If we found an MDDialog instance, dismiss it
|
|
195
|
+
if isinstance(current_widget, MDDialog):
|
|
196
|
+
current_widget.dismiss()
|
|
197
|
+
|
|
198
|
+
def add_profile(self, profile_name, button_instance):
|
|
199
|
+
# Access the text from the input box
|
|
200
|
+
print(f"New profile name: {profile_name}")
|
|
201
|
+
self.config['profiles'][profile_name] = dict()
|
|
202
|
+
self.set_profile(None, profile_name)
|
|
203
|
+
|
|
204
|
+
# Close the dialog using the button instance
|
|
205
|
+
self.close_dialog(button_instance)
|
|
206
|
+
|
|
207
|
+
def create_new_profile(self):
|
|
208
|
+
self.close_dropdown()
|
|
209
|
+
|
|
210
|
+
profile_input = MDTextField(
|
|
211
|
+
hint_text="Enter profile name",
|
|
212
|
+
size_hint_x=0.8,
|
|
213
|
+
pos_hint={"center_x": 0.5}
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
dialog = MDDialog(
|
|
217
|
+
title="New Profile",
|
|
218
|
+
type="custom",
|
|
219
|
+
content_cls=profile_input,
|
|
220
|
+
buttons=[
|
|
221
|
+
MDRaisedButton(
|
|
222
|
+
text="CANCEL",
|
|
223
|
+
on_release=self.close_dialog
|
|
224
|
+
),
|
|
225
|
+
MDRaisedButton(
|
|
226
|
+
text="OK",
|
|
227
|
+
on_release=lambda x: self.add_profile(profile_input.text, x)
|
|
228
|
+
)
|
|
229
|
+
],
|
|
230
|
+
)
|
|
231
|
+
dialog.open()
|
|
232
|
+
|
|
233
|
+
def close_dropdown(self):
|
|
234
|
+
profile_dropdown = self.root.ids['profile_dropdown']
|
|
235
|
+
profile_dropdown.dismiss()
|
|
236
|
+
|
|
237
|
+
def handle_dropdown(self, profile):
|
|
238
|
+
self.set_profile(None, profile)
|
|
239
|
+
self.close_dropdown()
|
|
240
|
+
|
|
241
|
+
def generate_profile_selector(self):
|
|
242
|
+
profile_dropdown = self.root.ids['profile_dropdown']
|
|
243
|
+
profile_selector = self.root.ids['profile_selector']
|
|
244
|
+
try:
|
|
245
|
+
profiles = list(self.config['profiles'].keys())
|
|
246
|
+
except KeyError:
|
|
247
|
+
profiles = ['DEFAULT']
|
|
248
|
+
max_length = 7
|
|
249
|
+
|
|
250
|
+
# Removing existing widgets
|
|
251
|
+
for child in profile_dropdown.children:
|
|
252
|
+
for widget in reversed(child.children):
|
|
253
|
+
child.remove_widget(widget)
|
|
254
|
+
|
|
255
|
+
btn = MDRaisedButton()
|
|
256
|
+
btn.text = '- New -'
|
|
257
|
+
btn.size_hint_y = None
|
|
258
|
+
btn.bind(on_release=lambda x: self.create_new_profile())
|
|
259
|
+
profile_dropdown.add_widget(btn)
|
|
260
|
+
|
|
261
|
+
for profile in profiles:
|
|
262
|
+
if len(profile) > max_length:
|
|
263
|
+
max_length = len(profile)
|
|
264
|
+
|
|
265
|
+
if profile != profile_selector.text.strip():
|
|
266
|
+
btn = MDRaisedButton()
|
|
267
|
+
btn.text = profile
|
|
268
|
+
btn.size_hint_y = None
|
|
269
|
+
btn.bind(on_release=lambda x, p=profile: self.handle_dropdown(p))
|
|
270
|
+
profile_dropdown.add_widget(btn)
|
|
271
|
+
|
|
272
|
+
number_of_whitespaces = max_length - len(profile_selector.text.strip())
|
|
273
|
+
profile_selector.text = profile_selector.text + (number_of_whitespaces + 1) * " "
|
|
274
|
+
|
|
275
|
+
def set_profile(self, dt=None, profile=None):
|
|
276
|
+
profile_selector = self.root.ids['profile_selector']
|
|
277
|
+
profile_name = "DEFAULT"
|
|
278
|
+
saved_inputs = list()
|
|
279
|
+
|
|
280
|
+
if profile is None:
|
|
281
|
+
profile_name = list(self.config['profiles'].keys())[0]
|
|
282
|
+
else:
|
|
283
|
+
profile_name = profile
|
|
284
|
+
|
|
285
|
+
profile_selector.text = profile_name
|
|
286
|
+
self.load_inputs()
|
|
287
|
+
self.generate_profile_selector()
|
|
148
288
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
289
|
+
def load_inputs(self):
|
|
290
|
+
profile_selector = self.root.ids['profile_selector']
|
|
291
|
+
profile_name = profile_selector.text.strip()
|
|
152
292
|
|
|
153
|
-
|
|
293
|
+
if profile_name in self.config['profiles']:
|
|
294
|
+
profile_values = self.config['profiles'][profile_name]
|
|
295
|
+
else:
|
|
296
|
+
profile_values = dict()
|
|
297
|
+
|
|
298
|
+
input_field_buttons = sum([x.children for x in self.root.ids['input_fields_container'].children], [])
|
|
299
|
+
input_fields = [x for x in input_field_buttons if isinstance(x, TextInput)]
|
|
300
|
+
checkboxes = [x for x in input_field_buttons if isinstance(x, MDCheckbox) and getattr(x, 'secret_checkbox', False)]
|
|
301
|
+
|
|
302
|
+
if len(profile_values.items()) > 1:
|
|
303
|
+
for name, (text, secret_state) in profile_values.items():
|
|
154
304
|
# Set text for TextInput
|
|
155
305
|
for input_field in input_fields:
|
|
156
306
|
if input_field.parent.text_input_id == name:
|
|
157
307
|
input_field.text = text
|
|
158
308
|
break # Found the matching input field, no need to continue the loop
|
|
159
309
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
310
|
+
# Set state for corresponding checkbox and adjust text visibility
|
|
311
|
+
for checkbox in checkboxes:
|
|
312
|
+
if checkbox.parent.text_input_id == name:
|
|
313
|
+
checkbox.active = secret_state
|
|
314
|
+
# Assuming MDTextField is a sibling or accessible as input_field here
|
|
315
|
+
# and setting its "password" property based on checkbox state
|
|
316
|
+
if secret_state:
|
|
317
|
+
input_field.password = True # Hide text if checkbox is checked
|
|
318
|
+
else:
|
|
319
|
+
input_field.password = False # Show text otherwise
|
|
320
|
+
break # Found the matching checkbox, no need to continue the loop
|
|
321
|
+
else:
|
|
322
|
+
for input_field in input_fields:
|
|
323
|
+
input_field.text = text = ""
|
|
171
324
|
|
|
172
|
-
except FileNotFoundError:
|
|
173
|
-
pass
|
|
174
|
-
except AttributeError:
|
|
175
|
-
pass
|
|
176
|
-
except json.JSONDecodeError:
|
|
177
|
-
# Handle cases where the JSON file is empty or corrupted
|
|
178
|
-
pass
|
|
179
|
-
except ValueError:
|
|
180
|
-
# Handle cases where the JSON file is empty or corrupted
|
|
181
|
-
pass
|
|
182
325
|
|
|
183
326
|
def save_inputs(self):
|
|
327
|
+
profile_selector = self.root.ids['profile_selector']
|
|
328
|
+
profile_name = profile_selector.text.strip()
|
|
329
|
+
|
|
184
330
|
# Assuming `input_field_buttons` contains all relevant child widgets,
|
|
185
331
|
# including both TextInput and MDCheckbox widgets.
|
|
186
332
|
input_field_buttons = sum([x.children for x in self.root.ids['input_fields_container'].children], [])
|
|
@@ -201,11 +347,11 @@ class KeyboardPasterApp(MDApp):
|
|
|
201
347
|
# Store the tuple of text and secret_state in the dictionary.
|
|
202
348
|
input_fields[child.parent.text_input_id] = (child.text, secret_state)
|
|
203
349
|
|
|
204
|
-
# Save the dictionary to a JSON file.
|
|
205
|
-
with open("saved_inputs.json", "w") as file:
|
|
206
|
-
json.dump(input_fields, file)
|
|
207
350
|
|
|
208
|
-
|
|
351
|
+
self.config['profiles'][profile_name] = input_fields
|
|
352
|
+
self.write_config()
|
|
353
|
+
|
|
354
|
+
def type_text(self, input_text, _checkbox):
|
|
209
355
|
if not input_text:
|
|
210
356
|
# print("No text found")
|
|
211
357
|
return
|
|
@@ -219,6 +365,15 @@ class KeyboardPasterApp(MDApp):
|
|
|
219
365
|
mod_delay = float(self.root.ids["mod_delay"].value)
|
|
220
366
|
type_string_with_delay(input_text, start_delay=start_delay, mod_delay=mod_delay, layout=self.layout, end_line=end_line)
|
|
221
367
|
|
|
368
|
+
@staticmethod
|
|
369
|
+
def copy_text(input_text, _checkbox):
|
|
370
|
+
if not input_text:
|
|
371
|
+
# print("No text found")
|
|
372
|
+
return
|
|
373
|
+
|
|
374
|
+
cmd = 'echo ' + input_text.strip() + '|clip'
|
|
375
|
+
subprocess.check_call(cmd, shell=True)
|
|
376
|
+
|
|
222
377
|
@staticmethod
|
|
223
378
|
def hide_text(_input_text, _checkbox):
|
|
224
379
|
if _checkbox.state == "down":
|
|
@@ -28,22 +28,58 @@
|
|
|
28
28
|
text: "Secret"
|
|
29
29
|
size_hint: 0.1, 1
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
on_press: app.
|
|
31
|
+
MDIconButton:
|
|
32
|
+
icon: "keyboard"
|
|
33
|
+
on_press: app.type_text(_text_input.text, _cb_execute)
|
|
34
34
|
|
|
35
|
+
MDIconButton:
|
|
36
|
+
icon: "content-copy"
|
|
37
|
+
on_press: app.copy_text(_text_input.text, _cb_execute)
|
|
38
|
+
|
|
39
|
+
<ProfileButton@MDFlatButton>:
|
|
40
|
+
text: "DEFAULT"
|
|
41
|
+
size_hint_y: None
|
|
42
|
+
height: 44
|
|
43
|
+
on_release:
|
|
44
|
+
root.select(self.text)
|
|
45
|
+
|
|
46
|
+
<CustomDropDown@DropDown>:
|
|
35
47
|
|
|
36
48
|
MDBoxLayout:
|
|
37
49
|
orientation: "vertical"
|
|
38
50
|
|
|
39
51
|
MDTopAppBar:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
MDBoxLayout:
|
|
53
|
+
orientation: "horizontal"
|
|
54
|
+
|
|
55
|
+
MDLabel:
|
|
56
|
+
id: title_bar
|
|
57
|
+
text: "Keyboard Paster"
|
|
58
|
+
halign: "center"
|
|
59
|
+
theme_text_color: "Custom"
|
|
60
|
+
text_color: 1, 1, 1, 1
|
|
61
|
+
font_size: '30sp'
|
|
62
|
+
|
|
63
|
+
MDBoxLayout:
|
|
64
|
+
orientation: "horizontal"
|
|
65
|
+
size_hint_x: .3
|
|
66
|
+
|
|
67
|
+
MDLabel:
|
|
68
|
+
text: "Profile:"
|
|
69
|
+
halign: 'right'
|
|
70
|
+
theme_text_color: "Custom"
|
|
71
|
+
text_color: 1, 1, 1, 1
|
|
72
|
+
font_size: '22sp'
|
|
73
|
+
|
|
74
|
+
MDFlatButton:
|
|
75
|
+
id: profile_selector
|
|
76
|
+
text: "DEFAULT"
|
|
77
|
+
pos_hint: {'center_y': 0.5}
|
|
78
|
+
theme_text_color: "Custom"
|
|
79
|
+
text_color: 1, 1, 1, 1
|
|
80
|
+
font_size: '22sp'
|
|
81
|
+
on_parent: profile_dropdown.dismiss()
|
|
82
|
+
on_release: profile_dropdown.open(self)
|
|
47
83
|
|
|
48
84
|
MDBoxLayout:
|
|
49
85
|
orientation: "vertical"
|
|
@@ -129,8 +165,9 @@ MDBoxLayout:
|
|
|
129
165
|
text: str(round(mod_delay.value, 1)) + " seconds"
|
|
130
166
|
theme_text_color: "Secondary"
|
|
131
167
|
|
|
132
|
-
|
|
133
|
-
|
|
134
168
|
MDRaisedButton:
|
|
135
169
|
text: "Save Inputs"
|
|
136
170
|
on_release: app.save_inputs()
|
|
171
|
+
|
|
172
|
+
CustomDropDown:
|
|
173
|
+
id: profile_dropdown
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
app_version = "0.1.18"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
app_version = "0.1.16"
|
|
File without changes
|
{keyboardpaster-0.1.16 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|