pake-cli 3.15.7 → 3.16.1

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.
@@ -240,20 +240,18 @@ window.addEventListener("DOMContentLoaded", (_event) => {
240
240
  position: fixed !important;
241
241
  top: 12px !important;
242
242
  right: 16px !important;
243
+ left: auto !important;
244
+ width: 182px !important;
243
245
  }
244
246
 
245
- #react-root [data-testid="sidebarColumn"] input[placeholder="Search"] {
246
- width: 150px;
247
+ #react-root [data-testid="sidebarColumn"] form[role="search"] input[data-testid="SearchBox_Search_Input"] {
248
+ width: 100% !important;
247
249
  }
248
250
 
249
251
  #react-root [data-testid="sidebarColumn"] form[role="search"]:focus-within {
250
252
  width: 280px !important;
251
253
  backdrop-filter: blur(12px) !important;
252
254
  }
253
-
254
- #react-root [data-testid="sidebarColumn"] input[placeholder="Search"]:focus {
255
- width: 234px !important;
256
- }
257
255
  }
258
256
 
259
257
  @media only screen and (min-width: 1265px) {
@@ -262,10 +260,12 @@ window.addEventListener("DOMContentLoaded", (_event) => {
262
260
  position: fixed !important;
263
261
  top: 12px !important;
264
262
  right: 16px !important;
263
+ left: auto !important;
264
+ width: 182px !important;
265
265
  }
266
266
 
267
- #react-root [data-testid="sidebarColumn"] input[placeholder="Search"] {
268
- width: 150px;
267
+ #react-root [data-testid="sidebarColumn"] form[role="search"] input[data-testid="SearchBox_Search_Input"] {
268
+ width: 100% !important;
269
269
  }
270
270
 
271
271
  #react-root [data-testid="sidebarColumn"] form[role="search"]:focus-within {
@@ -273,10 +273,6 @@ window.addEventListener("DOMContentLoaded", (_event) => {
273
273
  backdrop-filter: blur(12px) !important;
274
274
  }
275
275
 
276
- #react-root [data-testid="sidebarColumn"] input[placeholder="Search"]:focus {
277
- width: 328px !important;
278
- }
279
-
280
276
  #react-root div[style*="left: -12px"] {
281
277
  left: unset !important;
282
278
  }
@@ -324,9 +320,14 @@ window.addEventListener("DOMContentLoaded", (_event) => {
324
320
  padding-top: 36px;
325
321
  }
326
322
  `;
327
- const contentStyleElement = document.createElement("style");
328
- contentStyleElement.textContent = contentCSS;
329
- document.head.appendChild(contentStyleElement);
323
+ if (typeof window.__PAKE_INJECT_STYLE__ === "function") {
324
+ window.__PAKE_INJECT_STYLE__(contentCSS, "pake-content-style");
325
+ } else {
326
+ const contentStyleElement = document.createElement("style");
327
+ contentStyleElement.id = "pake-content-style";
328
+ contentStyleElement.textContent = contentCSS;
329
+ document.head.appendChild(contentStyleElement);
330
+ }
330
331
 
331
332
  // Top spacing adapts to head-hiding scenarios
332
333
  const topPaddingCSS = `
@@ -503,8 +504,7 @@ window.addEventListener("DOMContentLoaded", (_event) => {
503
504
  `;
504
505
  const isMac = /Mac/i.test(navigator.userAgent);
505
506
  if (hasImmersiveHeader(window["pakeConfig"])) {
506
- const topPaddingStyleElement = document.createElement("style");
507
- topPaddingStyleElement.textContent = isMac
507
+ const topPaddingCSSForPlatform = isMac
508
508
  ? topPaddingCSS
509
509
  : `
510
510
  #pake-top-dom:active {
@@ -525,6 +525,16 @@ window.addEventListener("DOMContentLoaded", (_event) => {
525
525
  z-index: 99999;
526
526
  }
527
527
  `;
528
- document.head.appendChild(topPaddingStyleElement);
528
+ if (typeof window.__PAKE_INJECT_STYLE__ === "function") {
529
+ window.__PAKE_INJECT_STYLE__(
530
+ topPaddingCSSForPlatform,
531
+ "pake-top-padding-style",
532
+ );
533
+ } else {
534
+ const topPaddingStyleElement = document.createElement("style");
535
+ topPaddingStyleElement.id = "pake-top-padding-style";
536
+ topPaddingStyleElement.textContent = topPaddingCSSForPlatform;
537
+ document.head.appendChild(topPaddingStyleElement);
538
+ }
529
539
  }
530
540
  });
@@ -0,0 +1,100 @@
1
+ (function () {
2
+ const INJECT_STYLE_KEY = "__PAKE_INJECT_STYLE__";
3
+ const adoptedSheetsById = new Map();
4
+
5
+ if (typeof window[INJECT_STYLE_KEY] === "function") {
6
+ return;
7
+ }
8
+
9
+ function containsImport(css) {
10
+ // Skip comments, strings and escaped delimiters; decode at-keyword escapes. Import
11
+ // rules are case-insensitive and need not begin a line. replaceSync silently
12
+ // drops them, so a blocked sheet containing imports must keep its DOM path.
13
+ const tokens =
14
+ /\/\*[\s\S]*?(?:\*\/|$)|"(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|\\(?:[0-9a-f]{1,6}[\t\n\f\r ]?|[^\n\r\f])|@((?:[-\w\u0080-\uffff]|\\(?:[0-9a-f]{1,6}[\t\n\f\r ]?|[^\n\r\f]))+)/gi;
15
+ for (const token of css.matchAll(tokens)) {
16
+ if (!token[1]) continue;
17
+ const keyword = token[1].replace(
18
+ /\\([0-9a-f]{1,6})[\t\n\f\r ]?|\\([^\n\r\f])/gi,
19
+ (_escape, hex, character) => {
20
+ const code = hex ? parseInt(hex, 16) : 0;
21
+ return hex
22
+ ? String.fromCodePoint(code > 0 && code <= 0x10ffff ? code : 0xfffd)
23
+ : character;
24
+ },
25
+ );
26
+ if (keyword.toLowerCase() === "import") return true;
27
+ }
28
+ return false;
29
+ }
30
+
31
+ function injectWithAdoptedStyleSheet(css, id) {
32
+ try {
33
+ if (
34
+ typeof window.CSSStyleSheet !== "function" ||
35
+ !("adoptedStyleSheets" in document) ||
36
+ containsImport(css)
37
+ ) {
38
+ return null;
39
+ }
40
+
41
+ const currentSheets = document.adoptedStyleSheets;
42
+ const sheet = new window.CSSStyleSheet();
43
+ sheet.replaceSync(css);
44
+ document.adoptedStyleSheets = Array.from(currentSheets).concat(sheet);
45
+
46
+ if (!Array.from(document.adoptedStyleSheets).includes(sheet)) {
47
+ return null;
48
+ }
49
+
50
+ if (id) {
51
+ adoptedSheetsById.set(id, sheet);
52
+ }
53
+ return sheet;
54
+ } catch (_error) {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ window[INJECT_STYLE_KEY] = function (css, id) {
60
+ if (id) {
61
+ const existingElement = document.getElementById(id);
62
+ if (existingElement) {
63
+ return existingElement;
64
+ }
65
+
66
+ const existingSheet = adoptedSheetsById.get(id);
67
+ if (existingSheet) {
68
+ try {
69
+ if (Array.from(document.adoptedStyleSheets).includes(existingSheet)) {
70
+ return existingSheet;
71
+ }
72
+ } catch (_error) {
73
+ // Recreate the sheet when the document's adopted-sheet list is unavailable.
74
+ }
75
+ adoptedSheetsById.delete(id);
76
+ }
77
+ }
78
+
79
+ const style = document.createElement("style");
80
+ if (id) {
81
+ style.id = id;
82
+ }
83
+ style.textContent = css;
84
+ (document.head || document.body || document.documentElement)?.appendChild(
85
+ style,
86
+ );
87
+
88
+ // Preserve normal DOM cascade order, including custom CSS with imports.
89
+ // CSP-blocked style elements have no associated sheet. Only those need the
90
+ // constructable-sheet fallback; imports remain subject to the page's CSP.
91
+ if (!style.sheet) {
92
+ const sheet = injectWithAdoptedStyleSheet(css, id);
93
+ if (sheet) {
94
+ style.remove();
95
+ return sheet;
96
+ }
97
+ }
98
+ return style;
99
+ };
100
+ })();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "productName": "Weekly",
3
3
  "identifier": "com.pake.weekly",
4
- "version": "3.15.7",
4
+ "version": "3.16.1",
5
5
  "app": {
6
6
  "withGlobalTauri": true,
7
7
  "trayIcon": {
@@ -1,187 +0,0 @@
1
- // macOS TLS certificate bypass for `--ignore-certificate-errors`.
2
- //
3
- // On macOS the webview is a WKWebView, which ignores the Chromium
4
- // `--ignore-certificate-errors` flag entirely, so Pake's flag was a no-op here
5
- // (see the Windows/Linux vs macOS split in window.rs). wry's own
6
- // `WryNavigationDelegate` does not implement
7
- // `webView:didReceiveAuthenticationChallenge:completionHandler:`, so WKWebView
8
- // falls back to default validation and rejects self-signed certs.
9
- //
10
- // This installs a thin navigation-delegate proxy that accepts server trust only
11
- // for the configured target host and forwards every other selector to wry's
12
- // original delegate, so navigation policy, downloads, and page-load callbacks
13
- // keep working untouched. It is installed only when the user opts in via
14
- // `--ignore-certificate-errors`.
15
-
16
- use objc2::rc::{Retained, Weak};
17
- use objc2::runtime::{AnyObject, Bool, NSObject, NSObjectProtocol, Sel};
18
- use objc2::{class, define_class, msg_send, DefinedClass, MainThreadMarker, MainThreadOnly};
19
- use objc2_foundation::NSString;
20
- use std::ffi::c_void;
21
-
22
- // NSURLSessionAuthChallengeDisposition values.
23
- const USE_CREDENTIAL: isize = 0;
24
- const PERFORM_DEFAULT_HANDLING: isize = 1;
25
-
26
- pub struct PakeCertDelegateIvars {
27
- // wry's real navigation delegate; every non-challenge selector forwards here.
28
- inner: Weak<AnyObject>,
29
- allowed_host: String,
30
- }
31
-
32
- static CERT_BYPASS_ASSOCIATION_KEY: u8 = 0;
33
-
34
- fn hosts_match(allowed_host: &str, challenge_host: &str) -> bool {
35
- allowed_host
36
- .trim_end_matches('.')
37
- .eq_ignore_ascii_case(challenge_host.trim_end_matches('.'))
38
- }
39
-
40
- define_class!(
41
- #[unsafe(super(NSObject))]
42
- #[name = "PakeCertBypassDelegate"]
43
- #[thread_kind = MainThreadOnly]
44
- #[ivars = PakeCertDelegateIvars]
45
- struct PakeCertBypassDelegate;
46
-
47
- unsafe impl NSObjectProtocol for PakeCertBypassDelegate {}
48
-
49
- impl PakeCertBypassDelegate {
50
- #[unsafe(method(webView:didReceiveAuthenticationChallenge:completionHandler:))]
51
- fn did_receive_challenge(
52
- &self,
53
- _webview: &AnyObject,
54
- challenge: &AnyObject,
55
- handler: &block2::Block<dyn Fn(isize, *mut AnyObject)>,
56
- ) {
57
- unsafe {
58
- let space: *mut AnyObject = msg_send![challenge, protectionSpace];
59
- if space.is_null() {
60
- handler.call((PERFORM_DEFAULT_HANDLING, core::ptr::null_mut()));
61
- return;
62
- }
63
- let host: *mut NSString = msg_send![space, host];
64
- if host.is_null()
65
- || !hosts_match(&self.ivars().allowed_host, &(&*host).to_string())
66
- {
67
- handler.call((PERFORM_DEFAULT_HANDLING, core::ptr::null_mut()));
68
- return;
69
- }
70
- // Only server-trust challenges carry a non-null serverTrust; for
71
- // anything else (e.g. HTTP basic auth) defer to default handling.
72
- let server_trust: *mut AnyObject = msg_send![space, serverTrust];
73
- if server_trust.is_null() {
74
- handler.call((PERFORM_DEFAULT_HANDLING, core::ptr::null_mut()));
75
- return;
76
- }
77
- let credential: *mut AnyObject =
78
- msg_send![class!(NSURLCredential), credentialForTrust: server_trust];
79
- handler.call((USE_CREDENTIAL, credential));
80
- }
81
- }
82
-
83
- // WKWebView probes respondsToSelector: before calling optional delegate
84
- // methods, so report our own methods plus everything wry implements.
85
- #[unsafe(method(respondsToSelector:))]
86
- fn responds_to_selector(&self, selector: Sel) -> Bool {
87
- let responds: Bool = unsafe { msg_send![super(self), respondsToSelector: selector] };
88
- if responds.as_bool() {
89
- return Bool::YES;
90
- }
91
- self.ivars()
92
- .inner
93
- .load()
94
- .map(|inner| unsafe { msg_send![&*inner, respondsToSelector: selector] })
95
- .unwrap_or(Bool::NO)
96
- }
97
-
98
- // Fast-forward any selector we do not implement to wry's delegate.
99
- #[unsafe(method(forwardingTargetForSelector:))]
100
- fn forwarding_target(&self, _selector: Sel) -> *mut AnyObject {
101
- self.ivars()
102
- .inner
103
- .load()
104
- .map(|inner| Retained::as_ptr(&inner) as *mut AnyObject)
105
- .unwrap_or(core::ptr::null_mut())
106
- }
107
- }
108
- );
109
-
110
- impl PakeCertBypassDelegate {
111
- fn new(
112
- inner: &Retained<AnyObject>,
113
- allowed_host: String,
114
- mtm: MainThreadMarker,
115
- ) -> Retained<Self> {
116
- let this = mtm
117
- .alloc::<PakeCertBypassDelegate>()
118
- .set_ivars(PakeCertDelegateIvars {
119
- inner: Weak::from_retained(inner),
120
- allowed_host,
121
- });
122
- unsafe { msg_send![super(this), init] }
123
- }
124
- }
125
-
126
- /// Replace the WKWebView's navigation delegate with a proxy that accepts
127
- /// invalid TLS certificates for the configured target host, then navigate to
128
- /// that target. `webview_ptr` is the raw `WKWebView` from
129
- /// `PlatformWebview::inner()`. Returns false if setup cannot be completed.
130
- pub fn install_cert_bypass_and_navigate(
131
- webview_ptr: *mut c_void,
132
- allowed_host: String,
133
- target_url: String,
134
- ) -> bool {
135
- if webview_ptr.is_null() || allowed_host.is_empty() || target_url.is_empty() {
136
- return false;
137
- }
138
- let Some(mtm) = MainThreadMarker::new() else {
139
- return false;
140
- };
141
- unsafe {
142
- let webview: &AnyObject = &*(webview_ptr as *const AnyObject);
143
- let existing: *mut AnyObject = msg_send![webview, navigationDelegate];
144
- if existing.is_null() {
145
- return false;
146
- }
147
- let Some(inner) = Retained::retain(existing) else {
148
- return false;
149
- };
150
- let proxy = PakeCertBypassDelegate::new(&inner, allowed_host, mtm);
151
- objc2::ffi::objc_setAssociatedObject(
152
- webview as *const AnyObject as *mut AnyObject,
153
- std::ptr::addr_of!(CERT_BYPASS_ASSOCIATION_KEY).cast(),
154
- Retained::as_ptr(&proxy) as *mut AnyObject,
155
- objc2::ffi::OBJC_ASSOCIATION_RETAIN_NONATOMIC,
156
- );
157
- let _: () = msg_send![webview, setNavigationDelegate: &*proxy];
158
-
159
- let target_url = NSString::from_str(&target_url);
160
- let ns_url: *mut AnyObject = msg_send![class!(NSURL), URLWithString: &*target_url];
161
- if ns_url.is_null() {
162
- return false;
163
- }
164
- let request: *mut AnyObject = msg_send![class!(NSURLRequest), requestWithURL: ns_url];
165
- if request.is_null() {
166
- return false;
167
- }
168
- let _: () = msg_send![webview, loadRequest: request];
169
- true
170
- }
171
- }
172
-
173
- #[cfg(test)]
174
- mod tests {
175
- use super::hosts_match;
176
-
177
- #[test]
178
- fn accepts_the_configured_host_case_insensitively() {
179
- assert!(hosts_match("INTERNAL.EXAMPLE.COM", "internal.example.com"));
180
- assert!(hosts_match("internal.example.com.", "internal.example.com"));
181
- }
182
-
183
- #[test]
184
- fn rejects_other_hosts() {
185
- assert!(!hosts_match("internal.example.com", "login.example.com"));
186
- }
187
- }