This commit is contained in:
daniel31x13
2026-05-11 20:00:21 -04:00
parent 8ed9fa595d
commit 3af2dc61af
8 changed files with 88 additions and 23 deletions
+3
View File
@@ -32,3 +32,6 @@ linkwarden.zip
**/build/
**/DerivedData/
*.xcuserdata
*.xcuserstate
xcuserdata/
*.xcuserdatad/
+1 -1
View File
@@ -2,7 +2,7 @@
"manifest_version": 3,
"minimum_chrome_version": "121",
"name": "Linkwarden",
"description": "The browser extension for Linkwarden.",
"description": "Save webpages, capture screenshots, and organize links in your Linkwarden collections.",
"homepage_url": "https://linkwarden.app/",
"version": "1.5.4",
"action": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Linkwarden",
"description": "The browser extension for Linkwarden.",
"description": "Save webpages, capture screenshots, and organize links in your Linkwarden collections.",
"homepage_url": "https://linkwarden.app/",
"version": "1.5.4",
"action": {
@@ -80,7 +80,7 @@
<window key="window" title="Linkwarden for Safari" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" animationBehavior="default" id="IQv-IB-iLA">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenNone="YES"/>
<rect key="contentRect" x="196" y="240" width="425" height="325"/>
<rect key="contentRect" x="196" y="240" width="425" height="440"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<connections>
<outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
@@ -99,11 +99,11 @@
<objects>
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="m2S-Jp-Qdl">
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
<rect key="frame" x="0.0" y="0.0" width="425" height="440"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<wkWebView wantsLayer="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eOr-cG-IQY">
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
<rect key="frame" x="0.0" y="0.0" width="425" height="440"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<wkWebViewConfiguration key="configuration">
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
@@ -19,19 +19,27 @@
height="128"
alt="Linkwarden for Safari icon"
/>
<h1>Linkwarden for Safari</h1>
<p class="description">
Save webpages, capture screenshots, and organize links in your Linkwarden
collections from Safari.
</p>
<p class="state-unknown">
You can turn on Linkwardens extension in Safari Extensions preferences.
Turn on Linkwarden for Safari in the Extensions section of Safari
Settings.
</p>
<p class="state-on">
Linkwarden for Safari is currently on. You can turn it off in Safari
Extensions preferences.
Linkwarden for Safari is currently on. You can turn it off in the
Extensions section of Safari Settings.
</p>
<p class="state-off">
Linkwarden for Safari is currently off. You can turn it on in Safari
Extensions preferences.
Linkwarden for Safari is currently off. You can turn it on in the
Extensions section of Safari Settings.
</p>
<button class="open-preferences">
Quit and Open Safari Extensions Preferences…
</button>
<p class="state-error" hidden>
Safari Settings did not open automatically. Open Safari, then go to Safari
&gt; Settings &gt; Extensions and enable Linkwarden.
</p>
<button class="open-preferences">Open Safari Settings</button>
</body>
</html>
@@ -3,7 +3,7 @@ function show(enabled, useSettingsInsteadOfPreferences) {
document.getElementsByClassName('state-on')[0].innerText = "Linkwarden for Safari is currently on. You can turn it off in the Extensions section of Safari Settings.";
document.getElementsByClassName('state-off')[0].innerText = "Linkwarden for Safari is currently off. You can turn it on in the Extensions section of Safari Settings.";
document.getElementsByClassName('state-unknown')[0].innerText = "You can turn on Linkwarden for Safari in the Extensions section of Safari Settings.";
document.getElementsByClassName('open-preferences')[0].innerText = "Quit and Open Safari Settings";
document.getElementsByClassName('open-preferences')[0].innerText = "Open Safari Settings";
}
if (typeof enabled === "boolean") {
@@ -15,7 +15,12 @@ function show(enabled, useSettingsInsteadOfPreferences) {
}
}
function showPreferencesError() {
document.querySelector(".state-error").hidden = false;
}
function openPreferences() {
document.querySelector(".state-error").hidden = true;
webkit.messageHandlers.controller.postMessage("open-preferences");
}
@@ -28,6 +28,23 @@ body {
text-align: center;
}
h1 {
margin: 0;
font: -apple-system-title1;
}
p {
margin: 0;
}
.description {
max-width: 460px;
}
.state-error {
color: #d70015;
}
body:not(.state-on, .state-off) :is(.state-on, .state-off) {
display: none;
}
@@ -9,11 +9,41 @@ import Cocoa
import SafariServices
import WebKit
let extensionBundleIdentifier = "app.linkwarden.safari-extension.Extension"
private let safariWebExtensionPointIdentifier = "com.apple.Safari.web-extension"
private let fallbackExtensionBundleIdentifier = "app.linkwarden.extension.safari"
private func resolveExtensionBundleIdentifier() -> String {
guard let builtInPlugInsURL = Bundle.main.builtInPlugInsURL,
let pluginURLs = try? FileManager.default.contentsOfDirectory(at: builtInPlugInsURL, includingPropertiesForKeys: nil) else {
return fallbackExtensionBundleIdentifier
}
for pluginURL in pluginURLs where pluginURL.pathExtension == "appex" {
guard let bundle = Bundle(url: pluginURL),
let bundleIdentifier = bundle.bundleIdentifier,
let extensionInfo = bundle.infoDictionary?["NSExtension"] as? [String: Any],
let extensionPointIdentifier = extensionInfo["NSExtensionPointIdentifier"] as? String,
extensionPointIdentifier == safariWebExtensionPointIdentifier else {
continue
}
return bundleIdentifier
}
return fallbackExtensionBundleIdentifier
}
class ViewController: NSViewController, WKNavigationDelegate, WKScriptMessageHandler {
@IBOutlet var webView: WKWebView!
private let extensionBundleIdentifier = resolveExtensionBundleIdentifier()
private var useSettingsInsteadOfPreferences: Bool {
if #available(macOS 13, *) {
return true
}
return false
}
override func viewDidLoad() {
super.viewDidLoad()
@@ -27,16 +57,12 @@ class ViewController: NSViewController, WKNavigationDelegate, WKScriptMessageHan
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
SFSafariExtensionManager.getStateOfSafariExtension(withIdentifier: extensionBundleIdentifier) { (state, error) in
guard let state = state, error == nil else {
// Insert code to inform the user that something went wrong.
return
}
DispatchQueue.main.async {
if #available(macOS 13, *) {
webView.evaluateJavaScript("show(\(state.isEnabled), true)")
if let state = state, error == nil {
webView.evaluateJavaScript("show(\(state.isEnabled), \(self.useSettingsInsteadOfPreferences))")
} else {
webView.evaluateJavaScript("show(\(state.isEnabled), false)")
NSLog("Failed to get Safari extension state: \(error?.localizedDescription ?? "Unknown error")")
webView.evaluateJavaScript("show(null, \(self.useSettingsInsteadOfPreferences))")
}
}
}
@@ -49,6 +75,12 @@ class ViewController: NSViewController, WKNavigationDelegate, WKScriptMessageHan
SFSafariApplication.showPreferencesForExtension(withIdentifier: extensionBundleIdentifier) { error in
DispatchQueue.main.async {
if let error = error {
NSLog("Failed to open Safari extension settings: \(error.localizedDescription)")
self.webView.evaluateJavaScript("showPreferencesError()")
return
}
NSApplication.shared.terminate(nil)
}
}