stupidpythonbrowser 1.0.2__tar.gz → 1.0.3__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.
Files changed (19) hide show
  1. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/PKG-INFO +1 -1
  2. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pybrowser/__init__.py +1 -1
  3. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pybrowser/browser.py +43 -21
  4. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pybrowser/settings.py +38 -15
  5. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pyproject.toml +1 -1
  6. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/stupidpythonbrowser.egg-info/PKG-INFO +1 -1
  7. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/LICENSE +0 -0
  8. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/README.md +0 -0
  9. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pybrowser/doh_server.py +0 -0
  10. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pybrowser/incognito.py +0 -0
  11. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pybrowser/main.py +0 -0
  12. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pybrowser/task_manager.py +0 -0
  13. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/pybrowser/terminal.py +0 -0
  14. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/setup.cfg +0 -0
  15. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/stupidpythonbrowser.egg-info/SOURCES.txt +0 -0
  16. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/stupidpythonbrowser.egg-info/dependency_links.txt +0 -0
  17. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/stupidpythonbrowser.egg-info/entry_points.txt +0 -0
  18. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/stupidpythonbrowser.egg-info/requires.txt +0 -0
  19. {stupidpythonbrowser-1.0.2 → stupidpythonbrowser-1.0.3}/stupidpythonbrowser.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stupidpythonbrowser
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: Native macOS Chromium/Material 3 Web Browser in PyObjC & WebKit
5
5
  Author: avevall
6
6
  Classifier: Environment :: MacOS X :: Cocoa
@@ -3,7 +3,7 @@ PyBrowser: Native macOS Chromium/Material 3 Web Browser.
3
3
  Package Root.
4
4
  """
5
5
 
6
- __version__ = "1.0.2"
6
+ __version__ = "1.0.3"
7
7
  __all__ = [
8
8
  "browser",
9
9
  "settings",
@@ -455,12 +455,20 @@ COMMAND_CLICK_JS = """
455
455
 
456
456
  AUDIO_MONITOR_JS = """
457
457
  document.addEventListener('play', (e) => {
458
- if (e.target && !e.target.muted) window.webkit.messageHandlers.audio.postMessage({cmd:'audio_play'});
458
+ if (e.target && !e.target.muted && window.webkit?.messageHandlers?.audio) {
459
+ window.webkit.messageHandlers.audio.postMessage({cmd:'audio_play'});
460
+ }
461
+ }, true);
462
+ document.addEventListener('pause', () => {
463
+ if (window.webkit?.messageHandlers?.audio) window.webkit.messageHandlers.audio.postMessage({cmd:'audio_pause'});
464
+ }, true);
465
+ document.addEventListener('ended', () => {
466
+ if (window.webkit?.messageHandlers?.audio) window.webkit.messageHandlers.audio.postMessage({cmd:'audio_pause'});
459
467
  }, true);
460
- document.addEventListener('pause', () => window.webkit.messageHandlers.audio.postMessage({cmd:'audio_pause'}), true);
461
- document.addEventListener('ended', () => window.webkit.messageHandlers.audio.postMessage({cmd:'audio_pause'}), true);
462
468
  document.addEventListener('volumechange', (e) => {
463
- if (e.target && e.target.muted) window.webkit.messageHandlers.audio.postMessage({cmd:'audio_pause'});
469
+ if (e.target && e.target.muted && window.webkit?.messageHandlers?.audio) {
470
+ window.webkit.messageHandlers.audio.postMessage({cmd:'audio_pause'});
471
+ }
464
472
  }, true);
465
473
  """
466
474
 
@@ -490,7 +498,7 @@ document.addEventListener('submit', (e) => {
490
498
  if (!e.target || !e.target.querySelector) return;
491
499
  let pw = e.target.querySelector('input[type="password"]');
492
500
  let user = e.target.querySelector('input[type="text"], input[type="email"]');
493
- if (pw && pw.value) {
501
+ if (pw && pw.value && window.webkit?.messageHandlers?.bridge) {
494
502
  window.webkit.messageHandlers.bridge.postMessage({
495
503
  cmd: 'prompt_save_password', host: location.hostname,
496
504
  username: user ? user.value : 'unknown', password: pw.value
@@ -1108,22 +1116,22 @@ window.writeToTerm = b => {{
1108
1116
  safe_u = json.dumps(u)
1109
1117
  host = urllib.parse.urlparse(u).netloc
1110
1118
  fav_tag = f"<img src='https://www.google.com/s2/favicons?domain={urllib.parse.quote(host)}&sz=32' width='16' height='16' style='vertical-align:middle;margin-right:8px;border-radius:3px;' onerror='this.style.display=\"none\"'>" if host else ""
1111
- del_call = f"window.webkit.messageHandlers.bridge.postMessage({{cmd: 'delete_bookmark', url: {safe_u}}}); setTimeout(() => location.reload(), 100);"
1119
+ del_call = f"if(window.webkit?.messageHandlers?.bridge) window.webkit.messageHandlers.bridge.postMessage({{cmd: 'delete_bookmark', url: {safe_u}}}); setTimeout(() => location.reload(), 100);"
1112
1120
  items.append(f"<li style='display:flex;align-items:center;padding:6px 0;'><span style='flex:1;display:flex;align-items:center;'>{fav_tag}<a href='{html.escape(u)}' style='color:#1a73e8;text-decoration:none;'>{html.escape(t)}</a></span> <button onclick='{del_call}' style='margin-left:12px;padding:3px 8px;'>Delete</button></li>")
1113
1121
  rows = "".join(items)
1114
- return f"<!DOCTYPE html><html><body style='font-family:-apple-system,sans-serif;padding:30px;'><h2>Bookmarks</h2><div style='margin-bottom:15px;'><input id='t' placeholder='Title' style='padding:4px 8px;margin-right:8px;'><input id='u' placeholder='https://...' style='padding:4px 8px;margin-right:8px;width:260px;'><button onclick='window.webkit.messageHandlers.bridge.postMessage({{cmd:\"add_bookmark\",title:document.getElementById(\"t\").value,url:document.getElementById(\"u\").value}});setTimeout(()=>location.reload(),100)' style='padding:4px 12px;'>Add</button></div><ul style='list-style:none;padding:0;'>{rows or '<li>No bookmarks</li>'}</ul></body></html>"
1122
+ return f"<!DOCTYPE html><html><body style='font-family:-apple-system,sans-serif;padding:30px;'><h2>Bookmarks</h2><div style='margin-bottom:15px;'><input id='t' placeholder='Title' style='padding:4px 8px;margin-right:8px;'><input id='u' placeholder='https://...' style='padding:4px 8px;margin-right:8px;width:260px;'><button onclick='if(window.webkit?.messageHandlers?.bridge) window.webkit.messageHandlers.bridge.postMessage({{cmd:\"add_bookmark\",title:document.getElementById(\"t\").value,url:document.getElementById(\"u\").value}});setTimeout(()=>location.reload(),100)' style='padding:4px 12px;'>Add</button></div><ul style='list-style:none;padding:0;'>{rows or '<li>No bookmarks</li>'}</ul></body></html>"
1115
1123
 
1116
1124
  def get_history_html(self):
1117
1125
  records = query_history()
1118
1126
  items = []
1119
1127
  for r in records:
1120
- del_call = f"window.webkit.messageHandlers.bridge.postMessage({{cmd: 'delete_history_item', id: {r[0]}}}); this.closest('tr').remove();"
1128
+ del_call = f"if(window.webkit?.messageHandlers?.bridge) window.webkit.messageHandlers.bridge.postMessage({{cmd: 'delete_history_item', id: {r[0]}}}); this.closest('tr').remove();"
1121
1129
  items.append(f"<tr><td>{r[3]}</td><td><a href='{html.escape(r[1])}'>{html.escape(r[2])}</a></td><td><button onclick='{del_call}'>✕</button></td></tr>")
1122
1130
  rows = "".join(items)
1123
- return f"<!DOCTYPE html><html><body style='font-family:-apple-system,sans-serif;padding:30px;'><h2>History</h2><button onclick=\"window.webkit.messageHandlers.bridge.postMessage({{cmd:'clear_browsing_data',targets:['history']}});setTimeout(()=>location.reload(),100)\">Clear All</button><table style='width:100%;margin-top:15px;'>{rows or '<tr><td>No history</td></tr>'}</table></body></html>"
1131
+ return f"<!DOCTYPE html><html><body style='font-family:-apple-system,sans-serif;padding:30px;'><h2>History</h2><button onclick=\"if(window.webkit?.messageHandlers?.bridge) window.webkit.messageHandlers.bridge.postMessage({{cmd:'clear_browsing_data',targets:['history']}});setTimeout(()=>location.reload(),100)\">Clear All</button><table style='width:100%;margin-top:15px;'>{rows or '<tr><td>No history</td></tr>'}</table></body></html>"
1124
1132
 
1125
1133
  def get_downloads_html(self):
1126
- return "<!DOCTYPE html><html><body style='font-family:-apple-system,sans-serif;padding:30px;'><h2>Downloads</h2><div id='dl-list'>No active downloads.</div><script>const dls={};window.updateDownload=(id,name,path,status,progress)=>{dls[id]={name,path,status,progress};render()};function render(){const el=document.getElementById('dl-list');el.innerHTML=Object.values(dls).map(d=>`<div style='border:1px solid #ccc;padding:10px;margin-bottom:8px;'><b>${d.name}</b> (${d.status}) <button onclick=\"window.webkit.messageHandlers.bridge.postMessage({cmd:'dl_show',path:'${d.path}'})\">Show in Finder</button></div>`).join('')};window.webkit.messageHandlers.bridge.postMessage({cmd:'dl_request_sync'});</script></body></html>"
1134
+ return "<!DOCTYPE html><html><body style='font-family:-apple-system,sans-serif;padding:30px;'><h2>Downloads</h2><div id='dl-list'>No active downloads.</div><script>const dls={};window.updateDownload=(id,name,path,status,progress)=>{dls[id]={name,path,status,progress};render()};function render(){const el=document.getElementById('dl-list');el.innerHTML=Object.values(dls).map(d=>`<div style='border:1px solid #ccc;padding:10px;margin-bottom:8px;'><b>${d.name}</b> (${d.status}) <button onclick=\"if(window.webkit?.messageHandlers?.bridge) window.webkit.messageHandlers.bridge.postMessage({cmd:'dl_show',path:'${d.path}'})\">Show in Finder</button></div>`).join('')};if(window.webkit?.messageHandlers?.bridge) window.webkit.messageHandlers.bridge.postMessage({cmd:'dl_request_sync'});</script></body></html>"
1127
1135
 
1128
1136
  def get_extensions_html(self):
1129
1137
  scripts = get_installed_userscripts()
@@ -1132,8 +1140,8 @@ window.writeToTerm = b => {{
1132
1140
  f = s["file"]
1133
1141
  safe_f = json.dumps(f)
1134
1142
  checked = "checked" if s["enabled"] else ""
1135
- toggle_call = f"window.webkit.messageHandlers.bridge.postMessage({{cmd: 'toggle_script', file: {safe_f}, enabled: this.checked}})"
1136
- del_call = f"if(confirm('Delete?')){{window.webkit.messageHandlers.bridge.postMessage({{cmd: 'delete_script', file: {safe_f}}}); setTimeout(() => location.reload(), 100);}}"
1143
+ toggle_call = f"if(window.webkit?.messageHandlers?.bridge) window.webkit.messageHandlers.bridge.postMessage({{cmd: 'toggle_script', file: {safe_f}, enabled: this.checked}})"
1144
+ del_call = f"if(confirm('Delete?')){{if(window.webkit?.messageHandlers?.bridge) window.webkit.messageHandlers.bridge.postMessage({{cmd: 'delete_script', file: {safe_f}}}); setTimeout(() => location.reload(), 100);}}"
1137
1145
  items.append(
1138
1146
  f"<div style='border:1px solid #ccc;padding:10px;margin-bottom:8px;'>"
1139
1147
  f"<b>{html.escape(s['name'])}</b> ({html.escape(f)}) "
@@ -1142,7 +1150,7 @@ window.writeToTerm = b => {{
1142
1150
  f"</div>"
1143
1151
  )
1144
1152
  rows = "".join(items)
1145
- return f"<!DOCTYPE html><html><body style='font-family:-apple-system,sans-serif;padding:30px;'><h2>Extensions & Userscripts</h2><button onclick='let id=prompt(\"CRX ID or URL:\");if(id)window.webkit.messageHandlers.bridge.postMessage({{cmd:\"install_crx\",id:id}})'>Install CRX</button><div style='margin-top:15px;'>{rows or 'No userscripts installed.'}</div></body></html>"
1153
+ return f"<!DOCTYPE html><html><body style='font-family:-apple-system,sans-serif;padding:30px;'><h2>Extensions & Userscripts</h2><button onclick='let id=prompt(\"CRX ID or URL:\");if(id && window.webkit?.messageHandlers?.bridge)window.webkit.messageHandlers.bridge.postMessage({{cmd:\"install_crx\",id:id}})'>Install CRX</button><div style='margin-top:15px;'>{rows or 'No userscripts installed.'}</div></body></html>"
1146
1154
 
1147
1155
  # ============================================================================
1148
1156
  # WEBKIT SUBCLASS & IMAGE CONTEXT MENU
@@ -1270,7 +1278,7 @@ class OmniboxSuggestionView(Cocoa.NSView):
1270
1278
  opts = Cocoa.NSTrackingMouseMoved | Cocoa.NSTrackingActiveAlways | Cocoa.NSTrackingInVisibleRect
1271
1279
  self.tracking_area = Cocoa.NSTrackingArea.alloc().initWithRect_options_owner_userInfo_(Cocoa.NSZeroRect, opts, self, None)
1272
1280
  self.addTrackingArea_(self.tracking_area)
1273
- objc.super(OmniboxSuggestionView, self).updateTrackingAreas()
1281
+ objc.super(ChromeTabView, self).updateTrackingAreas() if hasattr(objc.super(ChromeTabView, self), 'updateTrackingAreas') else None
1274
1282
 
1275
1283
  def mouseMoved_(self, event):
1276
1284
  loc = self.convertPoint_fromView_(event.locationInWindow(), None)
@@ -2685,10 +2693,14 @@ class BrowserWindowController(Cocoa.NSWindowController):
2685
2693
  return
2686
2694
  elif name == "terminalResize":
2687
2695
  b = message.body()
2688
- TERMINAL_MANAGER.get_or_create(message.webView(), b.get("cols"), b.get("rows"))
2696
+ TERMINAL_MANAGER.get_or_create(
2697
+ message.webView(),
2698
+ b.get("cols") if hasattr(b, "get") else None,
2699
+ b.get("rows") if hasattr(b, "get") else None
2700
+ )
2689
2701
  return
2690
2702
  elif name == "audio":
2691
- b = message.body() if isinstance(message.body(), dict) else {}
2703
+ b = message.body() if hasattr(message.body(), "get") else {}
2692
2704
  has_a = (b.get("cmd") == "audio_play")
2693
2705
  for t in self.tabs:
2694
2706
  if t.get("webview") == message.webView():
@@ -2698,7 +2710,13 @@ class BrowserWindowController(Cocoa.NSWindowController):
2698
2710
  return
2699
2711
 
2700
2712
  body = message.body()
2701
- cmd = body.get("cmd") if isinstance(body, dict) else None
2713
+ cmd = None
2714
+ if hasattr(body, "get"):
2715
+ cmd = body.get("cmd")
2716
+ elif isinstance(body, dict):
2717
+ cmd = body.get("cmd")
2718
+ if cmd is not None:
2719
+ cmd = str(cmd)
2702
2720
 
2703
2721
  if cmd == "open_url":
2704
2722
  target_url = body.get("url")
@@ -2749,8 +2767,11 @@ class BrowserWindowController(Cocoa.NSWindowController):
2749
2767
  if wv:
2750
2768
  wv.evaluateJavaScript_completionHandler_(f"let pw = document.querySelector('input[type=password]'); let user = document.querySelector('input[type=text], input[type=email]'); if(pw) pw.value = '{pwd}'; if(user) user.value = '{saved_user}';", None)
2751
2769
  elif cmd == "save_setting":
2752
- key, val = body.get("key"), body.get("val")
2753
- val = sanitize_for_json(val)
2770
+ raw_key, raw_val = body.get("key"), body.get("val")
2771
+ if raw_key is None:
2772
+ return
2773
+ key = str(raw_key)
2774
+ val = sanitize_for_json(raw_val)
2754
2775
  if key == "download_path":
2755
2776
  val = os.path.expanduser(str(val).strip())
2756
2777
  SETTINGS.settings[key] = val
@@ -2809,7 +2830,7 @@ class BrowserWindowController(Cocoa.NSWindowController):
2809
2830
 
2810
2831
  res = panel.runModal()
2811
2832
  if res == Cocoa.NSModalResponseOK and panel.URLs():
2812
- chosen_path = panel.URLs()[0].path()
2833
+ chosen_path = str(panel.URLs()[0].path())
2813
2834
  if os.access(chosen_path, os.W_OK):
2814
2835
  SETTINGS.settings["download_path"] = chosen_path
2815
2836
  SETTINGS.save()
@@ -2839,7 +2860,8 @@ class BrowserWindowController(Cocoa.NSWindowController):
2839
2860
  dispatch_to_main(on_done)
2840
2861
  threading.Thread(target=fetch_task, daemon=True).start()
2841
2862
  elif cmd == "clear_browsing_data":
2842
- targets = body.get("targets", [])
2863
+ raw_targets = body.get("targets", [])
2864
+ targets = list(raw_targets) if hasattr(raw_targets, "__iter__") else []
2843
2865
  if "history" in targets:
2844
2866
  clear_history()
2845
2867
  if "favicons" in targets:
@@ -23,7 +23,7 @@ DEFAULT_URL = "https://www.google.com"
23
23
  DATA_DIR = os.path.expanduser("~/data")
24
24
  os.makedirs(DATA_DIR, exist_ok=True)
25
25
 
26
- _cur_dir = os.path.dirname(os.path.abspath(__file__))
26
+ _cur_dir = os.path.dirname(os.path.abspath(__file__)) if "__file__" in globals() else os.getcwd()
27
27
  _old_candidates = [
28
28
  os.path.abspath(os.path.join(_cur_dir, "..", "browser_data")),
29
29
  os.path.abspath(os.path.join(os.getcwd(), "browser_data")),
@@ -87,29 +87,40 @@ def sanitize_for_json(obj):
87
87
  """
88
88
  if obj is None:
89
89
  return None
90
- # Check booleans explicitly first because bool subclasses int in Python
90
+
91
91
  type_name = type(obj).__name__
92
- if isinstance(obj, bool) or "Boolean" in type_name or type_name == "bool":
92
+
93
+ # PyObjC booleans (__NSCFBoolean) must use boolValue() to accurately preserve False/True
94
+ if hasattr(obj, "boolValue") and ("Boolean" in type_name or "bool" in type_name.lower()):
95
+ return bool(obj.boolValue())
96
+
97
+ # Standard Python booleans
98
+ if isinstance(obj, bool) or type_name == "bool":
93
99
  return bool(obj)
100
+
101
+ # Number conversion for PyObjC NSNumber (e.g. float/double or int)
102
+ if hasattr(obj, "doubleValue") and not isinstance(obj, (int, float, str, bytes)):
103
+ val = obj.doubleValue()
104
+ return int(val) if val.is_integer() else val
105
+
94
106
  if isinstance(obj, (int, float)):
95
107
  return obj
108
+
96
109
  if isinstance(obj, (str, bytes)):
97
110
  return str(obj)
111
+
98
112
  if hasattr(obj, "items") or isinstance(obj, dict):
99
113
  return {str(k): sanitize_for_json(v) for k, v in obj.items()}
114
+
100
115
  if hasattr(obj, "__iter__") or isinstance(obj, (list, tuple, set)):
101
116
  return [sanitize_for_json(i) for i in obj]
117
+
102
118
  try:
103
- # Number conversion for PyObjC NSNumber
104
- if hasattr(obj, "boolValue") and ("Boolean" in type_name or "bool" in str(type(obj)).lower()):
105
- return bool(obj.boolValue())
106
- if hasattr(obj, "doubleValue"):
107
- val = obj.doubleValue()
108
- return int(val) if val.is_integer() else val
109
119
  if hasattr(obj, "stringValue"):
110
120
  return str(obj.stringValue())
111
121
  except Exception:
112
122
  pass
123
+
113
124
  return str(obj)
114
125
 
115
126
  def validate_and_get_download_dir(target_path=None):
@@ -635,7 +646,9 @@ def get_settings_html(sb_count, base_count, custom_count, active_rules_count):
635
646
  window._toastTimer = setTimeout(() => t.classList.remove('show'), 2400);
636
647
  }}
637
648
  function saveSetting(key, val, msg) {{
638
- window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'save_setting', key: key, val: val }});
649
+ if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.bridge) {{
650
+ window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'save_setting', key: key, val: val }});
651
+ }}
639
652
  showToast(msg);
640
653
  }}
641
654
  function onDownloadInputChange(val) {{
@@ -662,12 +675,16 @@ def get_settings_html(sb_count, base_count, custom_count, active_rules_count):
662
675
  document.getElementById('custom-startup-row').style.display = (val === 'custom_url') ? 'flex' : 'none';
663
676
  }}
664
677
  function browseDownloadFolder() {{
665
- window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'choose_download_directory' }});
678
+ if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.bridge) {{
679
+ window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'choose_download_directory' }});
680
+ }}
666
681
  }}
667
682
  function updateAdblockFilters() {{
668
683
  const b = document.getElementById('update-filters-btn');
669
684
  b.innerText = "Syncing Lists..."; b.disabled = true;
670
- window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'update_adblock_filters' }});
685
+ if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.bridge) {{
686
+ window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'update_adblock_filters' }});
687
+ }}
671
688
  }}
672
689
  function searchDomain() {{
673
690
  const q = document.getElementById('inspector-query').value.trim();
@@ -675,7 +692,9 @@ def get_settings_html(sb_count, base_count, custom_count, active_rules_count):
675
692
  const box = document.getElementById('inspector-results');
676
693
  box.style.display = 'block';
677
694
  box.innerHTML = 'Searching blocklists...';
678
- window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'search_blocked_domains', query: q }});
695
+ if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.bridge) {{
696
+ window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'search_blocked_domains', query: q }});
697
+ }}
679
698
  }}
680
699
  window.renderBlockedResults = function(jsonStr) {{
681
700
  let items = [];
@@ -698,12 +717,16 @@ def get_settings_html(sb_count, base_count, custom_count, active_rules_count):
698
717
  if (document.getElementById('clr-favicons').checked) targets.push('favicons');
699
718
  if (document.getElementById('clr-dns').checked) targets.push('dns_cache');
700
719
  if (targets.length === 0) {{ showToast('Select at least one category'); return; }}
701
- window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'clear_browsing_data', targets: targets }});
720
+ if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.bridge) {{
721
+ window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'clear_browsing_data', targets: targets }});
722
+ }}
702
723
  showToast('Browsing data cleared');
703
724
  }}
704
725
  function executeResetDefaults() {{
705
726
  if (confirm("Restore all browser settings to factory defaults?")) {{
706
- window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'reset_defaults' }});
727
+ if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.bridge) {{
728
+ window.webkit.messageHandlers.bridge.postMessage({{ cmd: 'reset_defaults' }});
729
+ }}
707
730
  }}
708
731
  }}
709
732
  function jumpTo(id, navBtn) {{
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "stupidpythonbrowser"
7
- version = "1.0.2"
7
+ version = "1.0.3"
8
8
  description = "Native macOS Chromium/Material 3 Web Browser in PyObjC & WebKit"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stupidpythonbrowser
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: Native macOS Chromium/Material 3 Web Browser in PyObjC & WebKit
5
5
  Author: avevall
6
6
  Classifier: Environment :: MacOS X :: Cocoa