prettier, update to the dependencies (latest), remove nip26 and its artifacts, cleanup and renew

This commit is contained in:
Fishcake
2024-09-20 20:31:58 +09:00
parent 0477cc5cdf
commit e1c83597dd
37 changed files with 3136 additions and 2488 deletions

View File

@@ -188,7 +188,6 @@
941B042F2978CDF900CA291E /* Icon-16.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-16.png"; sourceTree = "<group>"; }; 941B042F2978CDF900CA291E /* Icon-16.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-16.png"; sourceTree = "<group>"; };
941B04302978CDF900CA291E /* Icon-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-64.png"; sourceTree = "<group>"; }; 941B04302978CDF900CA291E /* Icon-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-64.png"; sourceTree = "<group>"; };
944A6E02299F2FBB0032C2E3 /* experimental */ = {isa = PBXFileReference; lastKnownFileType = folder; path = experimental; sourceTree = "<group>"; }; 944A6E02299F2FBB0032C2E3 /* experimental */ = {isa = PBXFileReference; lastKnownFileType = folder; path = experimental; sourceTree = "<group>"; };
944A6E0D299F32070032C2E3 /* wizards */ = {isa = PBXFileReference; lastKnownFileType = folder; path = wizards; sourceTree = "<group>"; };
944A6E12299F39D30032C2E3 /* permission */ = {isa = PBXFileReference; lastKnownFileType = folder; path = permission; sourceTree = "<group>"; }; 944A6E12299F39D30032C2E3 /* permission */ = {isa = PBXFileReference; lastKnownFileType = folder; path = permission; sourceTree = "<group>"; };
944A6E38299F46270032C2E3 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; }; 944A6E38299F46270032C2E3 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
944A6E3E299F46D30032C2E3 /* NostoreApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostoreApp.swift; sourceTree = "<group>"; }; 944A6E3E299F46D30032C2E3 /* NostoreApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostoreApp.swift; sourceTree = "<group>"; };
@@ -294,7 +293,6 @@
children = ( children = (
9471E91E29A470C700EA623B /* event_history */, 9471E91E29A470C700EA623B /* event_history */,
944A6E12299F39D30032C2E3 /* permission */, 944A6E12299F39D30032C2E3 /* permission */,
944A6E0D299F32070032C2E3 /* wizards */,
944A6E02299F2FBB0032C2E3 /* experimental */, 944A6E02299F2FBB0032C2E3 /* experimental */,
948C69E4297F8BA600FB3574 /* options.build.css */, 948C69E4297F8BA600FB3574 /* options.build.css */,
948C69E1297F891F00FB3574 /* options.build.js */, 948C69E1297F891F00FB3574 /* options.build.js */,
@@ -461,7 +459,7 @@
attributes = { attributes = {
BuildIndependentTargetsInParallel = 1; BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1420; LastSwiftUpdateCheck = 1420;
LastUpgradeCheck = 1500; LastUpgradeCheck = 1600;
TargetAttributes = { TargetAttributes = {
941B03AE296FA90400CA291E = { 941B03AE296FA90400CA291E = {
CreatedOnToolsVersion = 14.2; CreatedOnToolsVersion = 14.2;
@@ -503,7 +501,6 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
944A6E0E299F32070032C2E3 /* wizards in Resources */,
941B03E0296FA90400CA291E /* Icon.png in Resources */, 941B03E0296FA90400CA291E /* Icon.png in Resources */,
944A6E03299F2FBB0032C2E3 /* experimental in Resources */, 944A6E03299F2FBB0032C2E3 /* experimental in Resources */,
941B03DE296FA90400CA291E /* Main.html in Resources */, 941B03DE296FA90400CA291E /* Main.html in Resources */,
@@ -520,7 +517,6 @@
files = ( files = (
941B03E1296FA90400CA291E /* Icon.png in Resources */, 941B03E1296FA90400CA291E /* Icon.png in Resources */,
941B03E3296FA90400CA291E /* Style.css in Resources */, 941B03E3296FA90400CA291E /* Style.css in Resources */,
944A6E0F299F32070032C2E3 /* wizards in Resources */,
941B03E5296FA90400CA291E /* Script.js in Resources */, 941B03E5296FA90400CA291E /* Script.js in Resources */,
944A6E14299F39D30032C2E3 /* permission in Resources */, 944A6E14299F39D30032C2E3 /* permission in Resources */,
941B03E9296FA90400CA291E /* Assets.xcassets in Resources */, 941B03E9296FA90400CA291E /* Assets.xcassets in Resources */,
@@ -534,7 +530,6 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
9471E91F29A470C700EA623B /* event_history in Resources */, 9471E91F29A470C700EA623B /* event_history in Resources */,
944A6E10299F32070032C2E3 /* wizards in Resources */,
941B0413297110F100CA291E /* background.build.js in Resources */, 941B0413297110F100CA291E /* background.build.js in Resources */,
944A6E15299F39D30032C2E3 /* permission in Resources */, 944A6E15299F39D30032C2E3 /* permission in Resources */,
948C69E82982DFE900FB3574 /* background.html in Resources */, 948C69E82982DFE900FB3574 /* background.html in Resources */,
@@ -572,7 +567,6 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
9471E92029A470C700EA623B /* event_history in Resources */, 9471E92029A470C700EA623B /* event_history in Resources */,
944A6E11299F32070032C2E3 /* wizards in Resources */,
941B0414297110F100CA291E /* background.build.js in Resources */, 941B0414297110F100CA291E /* background.build.js in Resources */,
944A6E16299F39D30032C2E3 /* permission in Resources */, 944A6E16299F39D30032C2E3 /* permission in Resources */,
948C69E92982DFE900FB3574 /* background.html in Resources */, 948C69E92982DFE900FB3574 /* background.html in Resources */,
@@ -860,7 +854,6 @@
941B0403296FA90400CA291E /* Debug */ = { 941B0403296FA90400CA291E /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
@@ -902,7 +895,6 @@
941B0404296FA90400CA291E /* Release */ = { 941B0404296FA90400CA291E /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
@@ -960,7 +952,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.2.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
@@ -993,7 +985,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.2.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
@@ -1011,7 +1003,6 @@
941B040A296FA90400CA291E /* Debug */ = { 941B040A296FA90400CA291E /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "macOS (App)/nostore.entitlements"; CODE_SIGN_ENTITLEMENTS = "macOS (App)/nostore.entitlements";
@@ -1048,7 +1039,6 @@
941B040B296FA90400CA291E /* Release */ = { 941B040B296FA90400CA291E /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "macOS (App)/nostore.entitlements"; CODE_SIGN_ENTITLEMENTS = "macOS (App)/nostore.entitlements";

View File

@@ -4,10 +4,10 @@ This is a [NIP-07][nip07] compatible extension for signing nostr events.
## Features ## Features
* Login with nostr (`getPublicKey`). - Login with nostr (`getPublicKey`).
* Post nostr event (`signEvent`). - Post nostr event (`signEvent`).
* Encrypted direct messages (`nip04.encrypt` and `nip04.decrypt`). - Encrypted direct messages (`nip04.encrypt` and `nip04.decrypt`).
* Multiple profiles. - Multiple profiles.
## Installation ## Installation

View File

@@ -1,20 +1,20 @@
{ {
"colors" : [ "colors": [
{ {
"color" : { "color": {
"color-space" : "display-p3", "color-space": "display-p3",
"components" : { "components": {
"alpha" : "1.000", "alpha": "1.000",
"blue" : "0.665", "blue": "0.665",
"green" : "0.271", "green": "0.271",
"red" : "0.509" "red": "0.509"
}
},
"idiom": "universal"
} }
}, ],
"idiom" : "universal" "info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,74 +1,74 @@
{ {
"images" : [ "images": [
{ {
"filename" : "iOS-Icon-1024.png", "filename": "iOS-Icon-1024.png",
"idiom" : "universal", "idiom": "universal",
"platform" : "ios", "platform": "ios",
"size" : "1024x1024" "size": "1024x1024"
}, },
{ {
"filename" : "Icon-16.png", "filename": "Icon-16.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "1x", "scale": "1x",
"size" : "16x16" "size": "16x16"
}, },
{ {
"filename" : "Icon-32.png", "filename": "Icon-32.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "2x", "scale": "2x",
"size" : "16x16" "size": "16x16"
}, },
{ {
"filename" : "Icon-32 1.png", "filename": "Icon-32 1.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "1x", "scale": "1x",
"size" : "32x32" "size": "32x32"
}, },
{ {
"filename" : "Icon-64.png", "filename": "Icon-64.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "2x", "scale": "2x",
"size" : "32x32" "size": "32x32"
}, },
{ {
"filename" : "Icon-128.png", "filename": "Icon-128.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "1x", "scale": "1x",
"size" : "128x128" "size": "128x128"
}, },
{ {
"filename" : "Icon-256.png", "filename": "Icon-256.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "2x", "scale": "2x",
"size" : "128x128" "size": "128x128"
}, },
{ {
"filename" : "Icon-256 1.png", "filename": "Icon-256 1.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "1x", "scale": "1x",
"size" : "256x256" "size": "256x256"
}, },
{ {
"filename" : "Icon-512.png", "filename": "Icon-512.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "2x", "scale": "2x",
"size" : "256x256" "size": "256x256"
}, },
{ {
"filename" : "Icon-512 1.png", "filename": "Icon-512 1.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "1x", "scale": "1x",
"size" : "512x512" "size": "512x512"
}, },
{ {
"filename" : "Icon-1024.png", "filename": "Icon-1024.png",
"idiom" : "mac", "idiom": "mac",
"scale" : "2x", "scale": "2x",
"size" : "512x512" "size": "512x512"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,6 +1,6 @@
{ {
"info" : { "info": {
"author" : "xcode", "author": "xcode",
"version" : 1 "version": 1
} }
} }

View File

@@ -1,20 +1,20 @@
{ {
"images" : [ "images": [
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "Icon-512.png", "filename": "Icon-512.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,6 +1,6 @@
{ {
"info" : { "info": {
"author" : "xcode", "author": "xcode",
"version" : 1 "version": 1
} }
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "ipad-menu.png", "filename": "ipad-menu.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "ipad-popup.png", "filename": "ipad-popup.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "ipad-url-bar.png", "filename": "ipad-url-bar.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,6 +1,6 @@
{ {
"info" : { "info": {
"author" : "xcode", "author": "xcode",
"version" : 1 "version": 1
} }
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "iphone-menu.png", "filename": "iphone-menu.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "iphone-popup.png", "filename": "iphone-popup.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "iphone-url-bar.png", "filename": "iphone-url-bar.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,6 +1,6 @@
{ {
"info" : { "info": {
"author" : "xcode", "author": "xcode",
"version" : 1 "version": 1
} }
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "default-popup.png", "filename": "default-popup.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,21 +1,21 @@
{ {
"images" : [ "images": [
{ {
"filename" : "toolbar-inactive.png", "filename": "toolbar-inactive.png",
"idiom" : "universal", "idiom": "universal",
"scale" : "1x" "scale": "1x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "2x" "scale": "2x"
}, },
{ {
"idiom" : "universal", "idiom": "universal",
"scale" : "3x" "scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
} }
],
"info" : {
"author" : "xcode",
"version" : 1
}
} }

View File

@@ -1,20 +1,39 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'"> <meta
http-equiv="Content-Security-Policy"
content="default-src 'self'"
/>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <meta
name="viewport"
content="width=device-width, initial-scale=1, user-scalable=no"
/>
<link rel="stylesheet" href="../Style.css"> <link rel="stylesheet" href="../Style.css" />
<script src="../Script.js" defer></script> <script src="../Script.js" defer></script>
</head> </head>
<body> <body>
<img src="../Icon.png" width="128" height="128" alt="Nostore Icon"> <img src="../Icon.png" width="128" height="128" alt="Nostore Icon" />
<p class="platform-ios">You can turn on Nostores Safari extension in Settings.</p> <p class="platform-ios">
<p class="platform-mac state-unknown">You can turn on Nostores extension in Safari Extensions preferences.</p> You can turn on Nostores Safari extension in Settings.
<p class="platform-mac state-on">Nostores extension is currently on. You can turn it off in Safari Extensions preferences.</p> </p>
<p class="platform-mac state-off">Nostores extension is currently off. You can turn it on in Safari Extensions preferences.</p> <p class="platform-mac state-unknown">
<button class="platform-mac open-preferences">Quit and Open Safari Extensions Preferences…</button> You can turn on Nostores extension in Safari Extensions
</body> preferences.
</p>
<p class="platform-mac state-on">
Nostores extension is currently on. You can turn it off in Safari
Extensions preferences.
</p>
<p class="platform-mac state-off">
Nostores extension is currently off. You can turn it on in Safari
Extensions preferences.
</p>
<button class="platform-mac open-preferences">
Quit and Open Safari Extensions Preferences…
</button>
</body>
</html> </html>

View File

@@ -2,13 +2,20 @@ function show(platform, enabled, useSettingsInsteadOfPreferences) {
document.body.classList.add(`platform-${platform}`); document.body.classList.add(`platform-${platform}`);
if (useSettingsInsteadOfPreferences) { if (useSettingsInsteadOfPreferences) {
document.getElementsByClassName('platform-mac state-on')[0].innerText = "Nostores extension is currently on. You can turn it off in the Extensions section of Safari Settings."; document.getElementsByClassName('platform-mac state-on')[0].innerText =
document.getElementsByClassName('platform-mac state-off')[0].innerText = "Nostores extension is currently off. You can turn it on in the Extensions section of Safari Settings."; 'Nostores extension is currently on. You can turn it off in the Extensions section of Safari Settings.';
document.getElementsByClassName('platform-mac state-unknown')[0].innerText = "You can turn on Nostores extension in the Extensions section of Safari Settings."; document.getElementsByClassName('platform-mac state-off')[0].innerText =
document.getElementsByClassName('platform-mac open-preferences')[0].innerText = "Quit and Open Safari Settings…"; 'Nostores extension is currently off. You can turn it on in the Extensions section of Safari Settings.';
document.getElementsByClassName(
'platform-mac state-unknown'
)[0].innerText =
'You can turn on Nostores extension in the Extensions section of Safari Settings.';
document.getElementsByClassName(
'platform-mac open-preferences'
)[0].innerText = 'Quit and Open Safari Settings…';
} }
if (typeof enabled === "boolean") { if (typeof enabled === 'boolean') {
document.body.classList.toggle(`state-on`, enabled); document.body.classList.toggle(`state-on`, enabled);
document.body.classList.toggle(`state-off`, !enabled); document.body.classList.toggle(`state-off`, !enabled);
} else { } else {
@@ -18,7 +25,9 @@ function show(platform, enabled, useSettingsInsteadOfPreferences) {
} }
function openPreferences() { function openPreferences() {
webkit.messageHandlers.controller.postMessage("open-preferences"); webkit.messageHandlers.controller.postMessage('open-preferences');
} }
document.querySelector("button.open-preferences").addEventListener("click", openPreferences); document
.querySelector('button.open-preferences')
.addEventListener('click', openPreferences);

View File

@@ -1,12 +1,11 @@
import { import {
generatePrivateKey,
getPublicKey,
signEvent,
nip04, nip04,
nip19, nip19,
nip26, generateSecretKey,
getEventHash, getPublicKey,
finalizeEvent,
} from 'nostr-tools'; } from 'nostr-tools';
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import { import {
getProfileIndex, getProfileIndex,
@@ -14,7 +13,6 @@ import {
getProfile, getProfile,
getPermission, getPermission,
setPermission, setPermission,
feature,
} from './utilities/utils'; } from './utilities/utils';
import { saveEvent } from './utilities/db'; import { saveEvent } from './utilities/db';
@@ -40,15 +38,13 @@ browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
deny(message); deny(message);
return Promise.resolve(true); return Promise.resolve(true);
case 'generatePrivateKey': case 'generatePrivateKey':
return Promise.resolve(generatePrivateKey()); return Promise.resolve(generatePrivateKey_());
case 'savePrivateKey': case 'savePrivateKey':
return savePrivateKey(message.payload); return savePrivateKey(message.payload);
case 'getNpub': case 'getNpub':
return getNpub(message.payload); return getNpub(message.payload);
case 'getNsec': case 'getNsec':
return getNsec(message.payload); return getNsec(message.payload);
case 'createDelegation':
return createDelegation(message.payload);
case 'calcPubKey': case 'calcPubKey':
return Promise.resolve(getPublicKey(message.payload)); return Promise.resolve(getPublicKey(message.payload));
case 'npubEncode': case 'npubEncode':
@@ -63,7 +59,7 @@ browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
case 'nip04.decrypt': case 'nip04.decrypt':
case 'getRelays': case 'getRelays':
validations[uuid] = sendResponse; validations[uuid] = sendResponse;
setDelegation(message).then(() => ask(uuid, message)); ask(uuid, message);
setTimeout(() => { setTimeout(() => {
prompt.release?.(); prompt.release?.();
}, 10_000); }, 10_000);
@@ -88,6 +84,11 @@ async function forceRelease() {
} }
} }
async function generatePrivateKey_() {
const sk = generateSecretKey();
return bytesToHex(sk);
}
async function ask(uuid, { kind, host, payload }) { async function ask(uuid, { kind, host, payload }) {
await forceRelease(); // Clean up previous tab if it closed without cleaning itself up await forceRelease(); // Clean up previous tab if it closed without cleaning itself up
prompt.release = await prompt.mutex.acquire(); prompt.release = await prompt.mutex.acquire();
@@ -178,35 +179,32 @@ async function savePrivateKey([index, privKey]) {
privKey = nip19.decode(privKey).data; privKey = nip19.decode(privKey).data;
} }
let profiles = await get('profiles'); let profiles = await get('profiles');
profiles[index].privKey = privKey; profiles[index].privKey = bytesToHex(privKey);
await storage.set({ profiles }); await storage.set({ profiles });
return true; return true;
} }
async function getNsec(index) { async function getNsec(index) {
let profile = await getProfile(index); let profile = await getProfile(index);
let nsec = nip19.nsecEncode(profile.privKey); let nsec = nip19.nsecEncode(hexToBytes(profile.privKey));
return nsec; return nsec;
} }
async function getNpub(index) { async function getNpub(index) {
let profile = await getProfile(index); let profile = await getProfile(index);
let pubKey = getPublicKey(profile.privKey); let pubKey = getPublicKey(hexToBytes(profile.privKey));
let npub = nip19.npubEncode(pubKey); let npub = nip19.npubEncode(pubKey);
return npub; return npub;
} }
async function getPrivKey() { async function getPrivKey() {
let profile = await currentProfile(); let profile = await currentProfile();
return profile.privKey; return hexToBytes(profile.privKey);
} }
async function getPubKey() { async function getPubKey() {
let pi = await getProfileIndex(); let pi = await getProfileIndex();
let profile = await getProfile(pi); let profile = await getProfile(pi);
if (profile.delegate) {
return profile.delegator;
}
let privKey = await getPrivKey(); let privKey = await getPrivKey();
let pubKey = getPublicKey(privKey); let pubKey = getPublicKey(privKey);
return pubKey; return pubKey;
@@ -220,11 +218,8 @@ async function currentProfile() {
async function signEvent_(event, host) { async function signEvent_(event, host) {
event = JSON.parse(JSON.stringify(event)); event = JSON.parse(JSON.stringify(event));
let privKey = await getPrivKey(); let sk = await getPrivKey();
let pubKey = getPublicKey(privKey); event = finalizeEvent(event, sk);
event.pubkey = pubKey;
event.id = getEventHash(event);
event.sig = signEvent(event, privKey);
saveEvent({ saveEvent({
event, event,
metadata: { host, signed_at: Math.round(Date.now() / 1000) }, metadata: { host, signed_at: Math.round(Date.now() / 1000) },
@@ -253,42 +248,3 @@ async function getRelays() {
}); });
return relayObj; return relayObj;
} }
function createDelegation({
kind,
delegatorPrivKey,
delegateePubKey,
until,
since,
}) {
delegatorPrivKey = nip19.decode(delegatorPrivKey).data;
let delegation = nip26.createDelegation(delegatorPrivKey, {
pubkey: delegateePubKey,
until,
// kind,
// since,
});
return Promise.resolve(delegation);
}
async function setDelegation({ payload }) {
if (!payload) return;
let active = await feature('delegation');
if (!active) return;
let { delegate, delegation } = await currentProfile();
// Nothing to do if this is not a delegate
if (!delegate) {
return;
}
payload.tags = payload.tags || [];
payload.tags.push([
'delegation',
delegation.from,
delegation.cond,
delegation.sig,
]);
}

View File

@@ -1,142 +1,228 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script defer src="event_history.build.js"></script>
<link rel="stylesheet" href="/options.build.css" />
<title>Event History</title>
<head> <style>
<meta charset="UTF-8"> label {
<meta http-equiv="X-UA-Compatible" content="IE=edge"> display: block;
<meta name="viewport" content="width=device-width, initial-scale=1.0"> }
<script defer src="event_history.build.js"></script> </style>
<link rel="stylesheet" href="/options.build.css"> </head>
<title>Event History</title>
<style> <body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="eventLog">
label { <p>
display: block; <a href="/options.html" class="border-none hover:underline"
} >← Back</a
</style> >
</head> </p>
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="eventLog"> <h1 class="section-header">Event History</h1>
<p>
<a href="/options.html" class="border-none hover:underline">← Back</a>
</p>
<h1 class="section-header">Event History</h1> <div class="section">
<div class="section-header">Filters</div>
<div class="section"> <div class="grid grid-cols-2 xl:grid-cols-4 gap-4">
<div class="section-header">Filters</div> <div>
<label for="view">View</label>
<select
id="view"
class="input"
x-model="view"
@change="reload"
>
<option value="created_at">created_at</option>
<option value="kind">kind</option>
<option value="host">host</option>
<option value="pubkey">pubkey</option>
</select>
</div>
<div class="grid grid-cols-2 xl:grid-cols-4 gap-4"> <div>
<div> <label for="sort">Order</label>
<label for="view">View</label> <select
<select id="view" class="input" x-model="view" @change="reload"> id="sort"
<option value="created_at">created_at</option> x-model="sort"
<option value="kind">kind</option> class="input"
<option value="host">host</option> @change="reload"
<option value="pubkey">pubkey</option> >
</select> <option value="asc">Ascending</option>
</div> <option value="desc">Descending</option>
</select>
</div>
<div> <div>
<label for="sort">Order</label> <label for="max">Max</label>
<select id="sort" x-model="sort" class="input" @change="reload"> <input
<option value="asc">Ascending</option> type="number"
<option value="desc">Descending</option> x-model.number="max"
</select> @input.debounce.750ms="reload"
</div> class="input"
min="10"
/>
</div>
<div> <div></div>
<label for="max">Max</label>
<input type="number" x-model.number="max" @input.debounce.750ms="reload" class="input" min="10">
</div>
<div></div> <div x-show="view === 'created_at'" x-cloak>
<label for="fromCreatedAt">From</label>
<input
type="date"
id="fromCreatedAt"
x-model="fromCreatedAt"
class="input"
@change="reload"
/>
</div>
<div x-show="view === 'created_at'" x-cloak> <div x-show="view === 'created_at'" x-cloak>
<label for="fromCreatedAt">From</label> <label for="toCreatedAt">To</label>
<input type="date" id="fromCreatedAt" x-model="fromCreatedAt" class="input" @change="reload"> <input
</div> type="date"
id="toCreatedAt"
x-model="toCreatedAt"
class="input"
@change="reload"
/>
</div>
<div x-show="view === 'created_at'" x-cloak> <div x-show="view === 'kind'" x-cloak>
<label for="toCreatedAt">To</label> <label for="kindShortcut">Quick Select</label>
<input type="date" id="toCreatedAt" x-model="toCreatedAt" class="input" @change="reload"> <select
</div> id="kindShortcut"
class="input"
@change="quickKindSelect"
x-model="quickKind"
>
<option></option>
<template x-for="k in kinds">
<option :value="k[0]" x-text="k[1]"></option>
</template>
</select>
</div>
<div x-show="view === 'kind'" x-cloak> <div x-show="view === 'kind'" x-cloak>
<label for="kindShortcut">Quick Select</label> <label for="fromKind">From</label>
<select id="kindShortcut" class="input" @change="quickKindSelect" x-model="quickKind"> <input
<option></option> type="number"
<template x-for="k in kinds"> id="fromKind"
<option :value="k[0]" x-text="k[1]"></option> x-model.number="fromKind"
</template> class="input"
</select> @change="reload"
</div> />
</div>
<div x-show="view === 'kind'" x-cloak> <div x-show="view === 'kind'" x-cloak>
<label for="fromKind">From</label> <label for="toKind">To</label>
<input type="number" id="fromKind" x-model.number="fromKind" class="input" @change="reload"> <input
</div> type="number"
id="toKind"
x-model.number="toKind"
class="input"
@change="reload"
/>
</div>
<div x-show="view === 'kind'" x-cloak> <div x-show="view === 'host'" x-cloak>
<label for="toKind">To</label> <label for="host">Host</label>
<input type="number" id="toKind" x-model.number="toKind" class="input" @change="reload"> <select
</div> id="host"
class="input"
x-model="host"
@change="reload"
>
<option value=""></option>
<template x-for="h in allHosts">
<option :value="h" x-text="h"></option>
</template>
</select>
</div>
<div x-show="view === 'host'" x-cloak> <div x-show="view === 'pubkey'" x-cloak>
<label for="host">Host</label> <label for="profiles">Profiles</label>
<select id="host" class="input" x-model="host" @change="reload"> <select
<option value=""></option> id="profiles"
<template x-for="h in allHosts"> class="input"
<option :value="h" x-text="h"></option> x-model="profile"
</template> @change="pkFromProfile"
</select> >
</div> <option value=""></option>
<template x-for="p in profileNames">
<option :value="p" x-text="p"></option>
</template>
</select>
</div>
<div x-show="view === 'pubkey'" x-cloak> <div x-show="view === 'pubkey'" x-cloak>
<label for="profiles">Profiles</label> <label for="pubkey">Pubkey</label>
<select id="profiles" class="input" x-model="profile" @change="pkFromProfile"> <input
<option value=""></option> type="text"
<template x-for="p in profileNames"> class="input"
<option :value="p" x-text="p"></option> x-model="pubkey"
</template> @input.debounce="reload"
</select> />
</div> </div>
</div>
<div x-show="view === 'pubkey'" x-cloak> <div>
<label for="pubkey">Pubkey</label> <button class="button mt-3" @click="saveAll">Save all</button>
<input type="text" class="input" x-model="pubkey" @input.debounce="reload"> <button class="button mt-3" @click="deleteAll">
</div> Delete all
</div> </button>
</div>
</div>
<div> <div class="text-sm italic mt-1 text-right">
<button class="button mt-3" @click="saveAll">Save all</button> Click/tap event body to copy the raw event.
<button class="button mt-3" @click="deleteAll">Delete all</button> </div>
</div>
</div>
<div class="text-sm italic mt-1 text-right">Click/tap event body to copy the raw event.</div> <template x-for="(event, index) in events">
<div class="mt-3 border-solid border border-fuchsia-700 rounded-lg">
<template x-for="(event, index) in events"> <div
<div class="mt-3 border-solid border border-fuchsia-700 rounded-lg"> class="select-none flex cursor-pointer text-sm md:text-xl"
<div class="select-none flex cursor-pointer text-sm md:text-xl" @click="selected = selected === index ? null : index"
@click="selected = selected === index ? null : index"> >
<div class="flex-none w-14 p-4 font-extrabold" x-text="selected === index ? '-' : '+'"></div> <div
<div class="flex-1 w-64 p-4" x-text="formatDate(event.metadata.signed_at)"></div> class="flex-none w-14 p-4 font-extrabold"
<div class="flex-1 w-64 p-4" x-text="event.metadata.host"></div> x-text="selected === index ? '-' : '+'"
<div class="flex-1 w-64 p-4" x-text="formatKind(event.event.kind)"></div> ></div>
</div> <div
class="flex-1 w-64 p-4"
<div @click.prevent="copyEvent(index)" class="cursor-pointer"> x-text="formatDate(event.metadata.signed_at)"
<pre class="rounded-b-lg bg-slate-200 text-sm md:text-xl" x-html="highlight(event)" x-show="selected === index" ></div>
x-transition:enter.opacity.delay.75ms x-transition:leave.opacity x-cloak> <div
</pre> class="flex-1 w-64 p-4"
x-text="event.metadata.host"
</div> ></div>
</div> <div
</template> class="flex-1 w-64 p-4"
<div class="fixed top-0 right-0 bg-fuchsia-800 rounded-md p-4 text-white m-8 drop-shadow-md" x-show="copied" x-cloack> x-text="formatKind(event.event.kind)"
Event copied! ></div>
</div> </div>
</body>
<div @click.prevent="copyEvent(index)" class="cursor-pointer">
<pre
class="rounded-b-lg bg-slate-200 text-sm md:text-xl"
x-html="highlight(event)"
x-show="selected === index"
x-transition:enter.opacity.delay.75ms
x-transition:leave.opacity
x-cloak
></pre>
</div>
</div>
</template>
<div
class="fixed top-0 right-0 bg-fuchsia-800 rounded-md p-4 text-white m-8 drop-shadow-md"
x-show="copied"
x-cloack
>
Event copied!
</div>
</body>
</html> </html>

View File

@@ -1,34 +1,44 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="/options.build.css" />
<script src="/experimental/experimental.build.js" defer></script>
<title>Experimental Features</title>
</head>
<head> <body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="experimental">
<meta charset="UTF-8"> <p>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <a href="/options.html" class="border-none hover:underline"
<meta name="viewport" content="width=device-width, initial-scale=1.0"> >← Back</a
<link rel="stylesheet" href="/options.build.css"> >
<script src="/experimental/experimental.build.js" defer></script> </p>
<title>Experimental Features</title> <h1 class="text-3xl lg:text-6xl font-bold md:text-center">
</head> Experimental Features
</h1>
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="experimental"> <p class="mt-3 text-center font-bold italic">
<p> These things may only work partially, or not work at all. Caveat
<a href="/options.html" class="border-none hover:underline">← Back</a> emptor!
</p> </p>
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Experimental Features</h1>
<p class="mt-3 text-center font-bold italic">
These things may only work partially, or not
work at all. Caveat
emptor!
</p>
<template x-for="feature in features" :key="feature[0]">
<div class="mt-4">
<input class="checkbox" type="checkbox" :id="feature[0]" x-model="feature[1]"
@change="change(feature[0], feature[1])">
<label :for="feature[0]" x-text="feature[2]" class="font-bold"></label>
<p x-text="feature[3]" class="italic"></p>
</div>
</template>
</body>
<template x-for="feature in features" :key="feature[0]">
<div class="mt-4">
<input
class="checkbox"
type="checkbox"
:id="feature[0]"
x-model="feature[1]"
@change="change(feature[0], feature[1])"
/>
<label
:for="feature[0]"
x-text="feature[2]"
class="font-bold"
></label>
<p x-text="feature[3]" class="italic"></p>
</div>
</template>
</body>
</html> </html>

View File

@@ -2,9 +2,9 @@ import Alpine from 'alpinejs';
const FEATURES = [ const FEATURES = [
[ [
'delegation', 'none',
'NIP-26 Delegation Profiles', 'NIP-XX: None',
'Allow user to create delegated profiles that obey the NIP-26 standard. Requires client support.', 'Reserved for the future use.',
], ],
]; ];

View File

@@ -17,12 +17,8 @@
}, },
"content_scripts": [ "content_scripts": [
{ {
"js": [ "js": ["content.build.js"],
"content.build.js" "matches": ["<all_urls>"]
],
"matches": [
"<all_urls>"
]
} }
], ],
"action": { "action": {
@@ -39,10 +35,7 @@
"options_ui": { "options_ui": {
"page": "options.html" "page": "options.html"
}, },
"permissions": [ "permissions": ["storage", "clipboardWrite"],
"storage",
"clipboardWrite"
],
"web_accessible_resources": [ "web_accessible_resources": [
{ {
"resources": [ "resources": [
@@ -51,21 +44,16 @@
"options.build.js", "options.build.js",
"options.build.css", "options.build.css",
"options.html", "options.html",
"wizards/delegation/delegation.html",
"wizards/delegation/delegation.build.js",
"wizards/delegation/delegation.build.css",
"experimental/experimental.html", "experimental/experimental.html",
"experimental/experimental.build.js", "experimental/experimental.build.js",
"event_history/event_history.html", "event_history/event_history.html",
"event_history/event_history.build.js" "event_history/event_history.build.js"
], ],
"matches": [ "matches": ["<all_urls>"]
"<all_urls>"
]
} }
], ],
"content_security_policy": { "content_security_policy": {
"extension_pages": "script-src 'self' 'unsafe-eval'" "extension_pages": "object-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-src 'self'; font-src 'self'; media-src 'self'; child-src 'self';"
}, },
"browser_specific_settings": { "browser_specific_settings": {
"safari": { "safari": {

View File

@@ -2,46 +2,48 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
[x-cloak] { display: none; } [x-cloak] {
display: none;
}
@layer components { @layer components {
.button { .button {
/* Colors */ /* Colors */
@apply bg-fuchsia-900 hover:bg-fuchsia-800 active:bg-fuchsia-700 text-fuchsia-200 disabled:bg-gray-200 disabled:text-black; @apply bg-fuchsia-900 hover:bg-fuchsia-800 active:bg-fuchsia-700 text-fuchsia-200 disabled:bg-gray-200 disabled:text-black;
/* Sizing and padding */ /* Sizing and padding */
@apply rounded-lg p-1.5 md:w-24 min-w-fit text-center; @apply rounded-lg p-1.5 md:w-24 min-w-fit text-center;
} }
.input { .input {
/* Colors */ /* Colors */
@apply bg-fuchsia-200 text-fuchsia-800 disabled:bg-gray-200 disabled:text-black focus:border-fuchsia-800; @apply bg-fuchsia-200 text-fuchsia-800 disabled:bg-gray-200 disabled:text-black focus:border-fuchsia-800;
/* Sizing and padding */ /* Sizing and padding */
@apply rounded-lg p-1.5 lg:p-1.5 w-full md:w-64; @apply rounded-lg p-1.5 lg:p-1.5 w-full md:w-64;
} }
.checkbox { .checkbox {
/* Colors */ /* Colors */
@apply text-fuchsia-800 bg-fuchsia-200 rounded-full accent-fuchsia-200; @apply text-fuchsia-800 bg-fuchsia-200 rounded-full accent-fuchsia-200;
/* Sizing and padding */ /* Sizing and padding */
@apply w-4 h-4 lg:w-5 lg:h-5; @apply w-4 h-4 lg:w-5 lg:h-5;
} }
.section { .section {
@apply border-2 border-fuchsia-700 rounded-lg p-1 md:p-5 mt-6 shadow-md; @apply border-2 border-fuchsia-700 rounded-lg p-1 md:p-5 mt-6 shadow-md;
} }
.section-header { .section-header {
@apply text-2xl lg:text-5xl font-bold; @apply text-2xl lg:text-5xl font-bold;
} }
.subsection-header { .subsection-header {
@apply text-xl lg:text-4xl font-bold; @apply text-xl lg:text-4xl font-bold;
} }
a { a {
@apply border-2 border-dotted text-fuchsia-800 border-fuchsia-800 hover:border-transparent; @apply border-2 border-dotted text-fuchsia-800 border-fuchsia-800 hover:border-transparent;
} }
} }

View File

@@ -1,184 +1,261 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="options.build.css" />
<script defer src="options.build.js"></script>
</head>
<head> <body x-data="options" class="text-fuchsia-900 p-3.5 lg:p-32">
<meta charset="UTF-8"> <h1 class="text-3xl lg:text-6xl font-bold md:text-center">Settings</h1>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="options.build.css">
<script defer src="options.build.js"></script>
</head> <!-- PROFILES -->
<div class="mt-6">
<body x-data="options" class="text-fuchsia-900 p-3.5 lg:p-32"> <label for="profiles">Profile</label>
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Settings</h1> <br />
<select class="input" x-model.number="profileIndex" id="profiles">
<!-- PROFILES --> <template x-for="(name, index) in profileNames" :key="index">
<div class="mt-6"> <option x-text="name" :value="index"></option>
<label for="profiles">Profile</label> </template>
<br> </select>
<select class="input" x-model.number="profileIndex" id="profiles"> <div class="block md:inline p-3 pl-0 md:p-0">
<template x-for="(name, index) in profileNames" :key="index"> <button class="button" @click.prevent="newProfile">New</button>
<option x-text="name" :value="index"></option> <button class="button" @click.prevent="deleteProfile">
</template> Delete
</select> </button>
<div class="block md:inline p-3 pl-0 md:p-0"> </div>
<button class="button" @click.prevent="newProfile">New</button>
<button class="button" @click.prevent="newDelegated" x-show="delegationActive" x-cloak>New Delegate</button>
<button class="button" @click.prevent="deleteProfile">Delete</button>
</div>
</div>
<!-- KEYS -->
<div class="section">
<h2 class="section-header">Keys</h2>
<p class="text-sm italic">Provide your <code class="not-italic">nsec</code> or legacy (hexadecimal) private keys.
</p>
<form @submit.prevent="saveProfile">
<div class="mt-3">
<label for="profile-name">Profile Name</label>
<br>
<input x-model="profileName" type="text" class="input" autocapitalize="off" autocomplete="off" spellcheck="off">
</div>
<div class="mt-3" x-show="delegate" x-cloak>
<div class="mb-1">
<span class="text-red-700 font-bold">This is is a delegated profile.</span>
</div> </div>
<label for="delegator-pub-key">Delegator Public Key</label> <!-- KEYS -->
<br> <div class="section">
<input id="delegator-pub-key" x-model="delegator" class="input" autocapitalize="off" autocomplete="off" <h2 class="section-header">Keys</h2>
spellcheck="off" :disabled="delegate"> <p class="text-sm italic">
</div> Provide your <code class="not-italic">nsec</code> or legacy
(hexadecimal) private keys.
</p>
<form @submit.prevent="saveProfile">
<div class="mt-3">
<label for="profile-name">Profile Name</label>
<br />
<input
x-model="profileName"
type="text"
class="input"
autocapitalize="off"
autocomplete="off"
spellcheck="off"
/>
</div>
<div class="mt-3"> <div class="mt-3">
<label for="priv-key">Private Key</label> <label for="priv-key">Private Key</label>
<br> <br />
<input x-model="privKey" :type="visibilityClass" class="input" :class="validKeyClass" autocapitalize="off" <input
autocomplete="off" spellcheck="off" :disabled="delegate"> x-model="privKey"
<a href="" @click.prevent="visible = !visible" x-text="(visible && 'Hide') || 'Show'" class="border-none"></a> :type="visibilityClass"
</div> class="input"
:class="validKeyClass"
autocapitalize="off"
autocomplete="off"
spellcheck="off"
/>
<a
href=""
@click.prevent="visible = !visible"
x-text="(visible && 'Hide') || 'Show'"
class="border-none"
></a>
</div>
<div class="mt-3"> <div class="mt-3">
<label for="pub-key">Public Key</label> <label for="pub-key">Public Key</label>
<br> <br />
<input x-model="pubKey" type="text" class="input" disabled> <input
<a href="" class="border-none" @click.prevent="copyPubKey" x-text="copied ? 'Copied!' : 'Copy'"></a> x-model="pubKey"
</div> type="text"
class="input"
disabled
/>
<a
href=""
class="border-none"
@click.prevent="copyPubKey"
x-text="copied ? 'Copied!' : 'Copy'"
></a>
</div>
<div class="mt-3"> <div class="mt-3">
<button class="button" :disabled="!needsSave || !validKey" @click.prevent="saveProfile">Save</button> <button
</div> class="button"
</form> :disabled="!needsSave || !validKey"
@click.prevent="saveProfile"
>
Save
</button>
</div>
</form>
</div>
</div> <!-- RELAYS -->
<div class="section">
<h2 class="section-header">Relays</h2>
<p class="text-sm italic">Add relay suggestions for clients.</p>
<template x-if="hasRelays">
<table
class="mt-3 text-xs md:text-base table-auto md:table-fixed"
>
<thead class="font-bold text-lg">
<td class="p-2 text-center">URL</td>
<td class="p-2 text-center">Read</td>
<td class="p-2 text-center">Write</td>
<td class="p-2 text-center">Actions</td>
</thead>
<template x-for="(relay, index) in relays" :key="index">
<tr>
<td class="p-2 w-1/3" x-text="relay.url"></td>
<td class="p-2 text-center">
<input
class="checkbox"
type="checkbox"
x-model="relay.read"
@change="await saveRelays()"
/>
</td>
<td class="p-2 text-center">
<input
class="checkbox"
type="checkbox"
x-model="relay.write"
@change="await saveRelays()"
/>
</td>
<td class="p-2 text-center">
<button
class="button"
@click.prevent="await deleteRelay(index)"
>
Delete
</button>
</td>
</tr>
</template>
</table>
</template>
<!-- RELAYS --> <template x-if="!hasRelays">
<div class="section"> <div class="mt-3">
<h2 class="section-header">Relays</h2> There are no relays assigned to this profile.
<p class="text-sm italic">Add relay suggestions for clients.</p> </div>
<template x-if="hasRelays"> </template>
<table class="mt-3 text-xs md:text-base table-auto md:table-fixed">
<thead class="font-bold text-lg">
<td class="p-2 text-center">URL</td>
<td class="p-2 text-center">Read</td>
<td class="p-2 text-center">Write</td>
<td class="p-2 text-center">Actions</td>
</thead>
<template x-for="(relay, index) in relays" :key="index">
<tr>
<td class="p-2 w-1/3" x-text="relay.url"></td>
<td class="p-2 text-center">
<input class="checkbox" type="checkbox" x-model="relay.read" @change="await saveRelays()">
</td>
<td class="p-2 text-center">
<input class="checkbox" type="checkbox" x-model="relay.write" @change="await saveRelays()">
</td>
<td class="p-2 text-center">
<button class="button" @click.prevent="await deleteRelay(index)">Delete</button>
</td>
</tr>
</template>
</table>
</template>
<template x-if="!hasRelays"> <div class="mt-3" x-show="hasRecommendedRelays" x-cloak>
<div class="mt-3"> <select x-model="recommendedRelay" class="input">
There are no relays assigned to this profile. <option value="" disabled selected>
</div> Recommended Relays
</template> </option>
<template x-for="relay in recommendedRelays">
<option :value="relay" x-text="relay"></option>
</template>
</select>
</div>
<div class="mt-3" x-show="hasRecommendedRelays" x-cloak> <input
<select x-model="recommendedRelay" class="input"> class="mt-3 input"
<option value="" disabled selected>Recommended Relays</option> x-model="newRelay"
<template x-for="relay in recommendedRelays"> type="text"
<option :value="relay" x-text="relay"></option> @keyup.enter="await addRelay()"
</template> placeholder="wss://..."
</select> autocomplete="off"
</div> autocorrect="off"
autocapitalize="off"
spellcheck="off"
/>
<div class="block md:inline p-3 pl-0 md:p-0">
<button class="button" @click="await addRelay()">Add</button>
</div>
<div
class="text-red-500 font-bold"
x-show="urlError.length > 0"
x-text="urlError"
x-cloak
></div>
</div>
<input class="mt-3 input" x-model="newRelay" type="text" @keyup.enter="await addRelay()" placeholder="wss://..." <!-- PERMISSIONS -->
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="off"> <div class="section">
<div class="block md:inline p-3 pl-0 md:p-0"> <h2 class="section-header">App Permissions</h2>
<button class="button" @click="await addRelay()">Add</button> <p class="text-sm italic">
</div> Permissions granted to individual applications. Everything
<div class="text-red-500 font-bold" x-show="urlError.length > 0" x-text="urlError" x-cloak></div> defaults to <span class="font-bold">Ask</span> unless you have
</div> saved a different option.
</p>
<div class="mt-3" x-show="permHosts.length > 0">
<label for="app">Apps</label>
<br />
<select id="app" class="input" x-model="host">
<option value=""></option>
<template x-for="permHost in permHosts">
<option :value="permHost" x-text="permHost"></option>
</template>
</select>
</div>
<!-- PERMISSIONS --> <p x-show="permHosts.length === 0" x-cloak class="font-bold mt-3">
<div class="section"> You have not remembered any app requests yet.
<h2 class="section-header">App Permissions</h2> </p>
<p class="text-sm italic">
Permissions granted to individual applications.
Everything defaults to <span class="font-bold">Ask</span> unless you have saved a different option.
</p>
<div class="mt-3" x-show="permHosts.length > 0"> <table
<label for="app">Apps</label> class="mt-3 text-xs md:text-base table-fixed"
<br> x-show="hostPerms.length > 0"
<select id="app" class="input" x-model="host"> x-cloak
<option value=""></option> >
<template x-for="permHost in permHosts"> <thead class="font-bold text-lg">
<option :value="permHost" x-text="permHost"></option> <td class="p-2 text-center">App Request</td>
</template> <td class="p-2 text-center">Action</td>
</select> </thead>
</div> <template
x-for="[etype, humanName, perm] in hostPerms"
:key="etype"
>
<tr>
<td class="p-2 w-1/3 md:w-2/4" x-text="humanName"></td>
<td class="p-2 text-center">
<select
class="input"
:value="perm"
@change="await setPermission(host, etype, $event.target.value, profileIndex)"
>
<option value="ask">Ask</option>
<option value="allow">Allow</option>
<option value="deny">Deny</option>
</select>
</td>
</tr>
</template>
</table>
</div>
<p x-show="permHosts.length === 0" x-cloak class="font-bold mt-3">You have not remembered any app requests yet.</p> <div class="mt-6">
<button class="button" @click.prevent="closeOptions">Close</button>
<table class="mt-3 text-xs md:text-base table-fixed" x-show="hostPerms.length > 0" x-cloak> <button class="button" @click.prevent="clearData">
<thead class="font-bold text-lg"> Clear Data
<td class="p-2 text-center">App Request</td> </button>
<td class="p-2 text-center">Action</td> </div>
</thead>
<template x-for="[etype, humanName, perm] in hostPerms" :key="etype">
<tr>
<td class="p-2 w-1/3 md:w-2/4" x-text="humanName"></td>
<td class="p-2 text-center">
<select class="input" :value="perm"
@change="await setPermission(host, etype, $event.target.value, profileIndex)">
<option value="ask">Ask</option>
<option value="allow">Allow</option>
<option value="deny">Deny</option>
</select>
</td>
</tr>
</template>
</table>
</div>
<div class="mt-6">
<button class="button" @click.prevent="closeOptions">Close</button>
<button class="button" @click.prevent="clearData">Clear Data</button>
</div>
<div class="mt-6">
<a href="experimental/experimental.html" class="border-none hover:underline">Experimental
features →</a>
<br>
<a href="event_history/event_history.html" class="border-none hover:underline">Event history →</a>
</div>
</body>
<div class="mt-6">
<a
href="experimental/experimental.html"
class="border-none hover:underline"
>Experimental features →</a
>
<br />
<a
href="event_history/event_history.html"
class="border-none hover:underline"
>Event history →</a
>
</div>
</body>
</html> </html>

View File

@@ -16,8 +16,6 @@ import {
KINDS, KINDS,
humanPermission, humanPermission,
validateKey, validateKey,
feature,
getDelegator,
} from './utilities/utils'; } from './utilities/utils';
const log = console.log; const log = console.log;
@@ -42,10 +40,7 @@ Alpine.data('options', () => ({
host: '', host: '',
permHosts: [], permHosts: [],
hostPerms: [], hostPerms: [],
delegationActive: false,
visible: false, visible: false,
delegate: false,
delegator: '',
copied: false, copied: false,
setPermission, setPermission,
go, go,
@@ -77,8 +72,6 @@ Alpine.data('options', () => ({
await this.getProfileIndex(); await this.getProfileIndex();
this.setProfileIndexFromSearch(); this.setProfileIndexFromSearch();
await this.refreshProfile(); await this.refreshProfile();
this.delegationActive = await feature('delegation');
}, },
async refreshProfile() { async refreshProfile() {
@@ -88,7 +81,6 @@ Alpine.data('options', () => ({
await this.getNpub(); await this.getNpub();
await this.getRelays(); await this.getRelays();
await this.getPermissions(); await this.getPermissions();
await this.getDelegate();
}, },
// Profile functions // Profile functions
@@ -117,27 +109,12 @@ Alpine.data('options', () => ({
this.profileIndex = await getProfileIndex(); this.profileIndex = await getProfileIndex();
}, },
async getDelegate() {
let [delegate, delegator] = await getDelegator(this.profileIndex);
this.delegate = delegate;
this.delegator = await browser.runtime.sendMessage({
kind: 'npubEncode',
payload: delegator,
});
},
async newProfile() { async newProfile() {
let newIndex = await newProfile(); let newIndex = await newProfile();
await this.getProfileNames(); await this.getProfileNames();
this.profileIndex = newIndex; this.profileIndex = newIndex;
}, },
newDelegated() {
window.location = browser.runtime.getURL(
'wizards/delegation/delegation.html'
);
},
async deleteProfile() { async deleteProfile() {
if ( if (
confirm( confirm(

View File

@@ -1,43 +1,58 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script defer src="permission.build.js"></script>
<link rel="stylesheet" href="/options.build.css" />
<title>Permission Requested</title>
</head>
<head> <body x-data="permission">
<meta charset="UTF-8"> <div class="text-center">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <h1 class="section-header mt-5 text-center">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> App is requesting permission
<script defer src="permission.build.js"></script> </h1>
<link rel="stylesheet" href="/options.build.css"> <p class="mt-6 text-center">
<title>Permission Requested</title> The host
</head> <span class="text-lg font-bold" x-text="host"></span>
is requesting the following permission:
<span class="text-lg font-bold" x-text="humanPermission"></span
>.
</p>
<p x-show="isSigningEvent">
Event kind is
<a
:href="eventInfo.nip"
class="text-lg font-bold"
x-text="eventInfo.desc"
@click.prevent="await openNip()"
></a
>.
</p>
<body x-data="permission"> <template x-if="isSigningEvent">
<div class="text-center"> <div class="inline-block text-left">
<pre class="mt-6" x-html="humanEvent"></pre>
<h1 class="section-header mt-5 text-center">App is requesting permission</h1> </div>
<p class="mt-6 text-center"> </template>
The host
<span class="text-lg font-bold" x-text="host"></span>
is requesting the following permission:
<span class="text-lg font-bold" x-text="humanPermission"></span>.
</p>
<p x-show="isSigningEvent">
Event kind is <a :href="eventInfo.nip" class="text-lg font-bold" x-text="eventInfo.desc"
@click.prevent="await openNip()"></a>.
</p>
<template x-if="isSigningEvent">
<div class="inline-block text-left">
<pre class="mt-6" x-html="humanEvent"></pre>
</div>
</template>
<div class="mt-6 text-center">
<button class="button" @click="await allow()">Allow</button>
<button class="button" @click="await deny()">Deny</button>
<input class="checkbox" type="checkbox" id="remember" x-model="remember">
<label for="remember">Remember selection<span x-show="isSigningEvent" x-cloak> (by event kind)</span></label>
</div>
</div>
</body>
<div class="mt-6 text-center">
<button class="button" @click="await allow()">Allow</button>
<button class="button" @click="await deny()">Deny</button>
<input
class="checkbox"
type="checkbox"
id="remember"
x-model="remember"
/>
<label for="remember"
>Remember selection<span x-show="isSigningEvent" x-cloak>
(by event kind)</span
></label
>
</div>
</div>
</body>
</html> </html>

View File

@@ -2,7 +2,6 @@
color-scheme: light dark; color-scheme: light dark;
} }
body { body {
width: 300px; width: 300px;
padding: 15px; padding: 15px;

View File

@@ -1,55 +1,87 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" href="popup.css" />
<link rel="stylesheet" href="options.build.css" />
<script defer src="popup.build.js"></script>
</head>
<head> <body x-data="popup">
<meta charset="UTF-8"> <div class="profiles">
<meta name="viewport" content="width=device-width,initial-scale=1" /> <label for="profile">Active Profile</label>
<link rel="stylesheet" href="popup.css"> <div class="profile-buttons flex flex-row gap-2">
<link rel="stylesheet" href="options.build.css"> <div class="grow">
<script defer src="popup.build.js"></script> <select
</head> x-model.number="profileIndex"
name="profile"
<body x-data="popup"> id="profile"
<div class="profiles"> class="input"
<label for="profile">Active Profile</label> >
<div class="profile-buttons flex flex-row gap-2"> <template
<div class="grow"> x-for="(prof, index) in profileNames"
<select x-model.number="profileIndex" name="profile" id="profile" class="input"> :key="index"
<template x-for="(prof, index) in profileNames" :key="index"> >
<option x-text="prof" :value="index"></option> <option x-text="prof" :value="index"></option>
</template> </template>
</select> </select>
</div>
<button
class="button p-1.5"
@click.prevent="await copyNpub()"
style="display: block"
title="Copy npub"
>
<?xml version="1.0" encoding="UTF-8"?><svg
width="24px"
height="24px"
stroke-width="1.5"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
color="#000000"
>
<path
d="M19.4 20H9.6a.6.6 0 01-.6-.6V9.6a.6.6 0 01.6-.6h9.8a.6.6 0 01.6.6v9.8a.6.6 0 01-.6.6z"
stroke="#f5d0fe"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M15 9V4.6a.6.6 0 00-.6-.6H4.6a.6.6 0 00-.6.6v9.8a.6.6 0 00.6.6H9"
stroke="#f5d0fe"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</svg>
</button>
</div> </div>
<button class="button p-1.5" @click.prevent="await copyNpub()" style="display: block;" title="Copy npub"> </div>
<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" stroke-width="1.5"
viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"> <div class="relay" x-show="relayCount < 1 && showRelayReminder" x-cloak>
<path d="M19.4 20H9.6a.6.6 0 01-.6-.6V9.6a.6.6 0 01.6-.6h9.8a.6.6 0 01.6.6v9.8a.6.6 0 01-.6.6z" <span>
stroke="#f5d0fe" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> You do not have any relays setup for this profile. Would you
<path d="M15 9V4.6a.6.6 0 00-.6-.6H4.6a.6.6 0 00-.6.6v9.8a.6.6 0 00.6.6H9" stroke="#f5d0fe" like to add some recommended relays now?
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> </span>
</svg> <br />
<button class="button" @click="await addRelays()">
Add Relays
</button>
<button class="button" @click="noThanks">No Thanks</button>
</div>
<div class="help">
<button class="button p-1.5" @click="await openOptions()">
Settings
</button> </button>
</div> </div>
</div>
<div class="relay" x-show="relayCount < 1 && showRelayReminder" x-cloak>
<span>
You do not have any relays setup for this profile. Would you like to add some recommended
relays now?
</span>
<br>
<button class="button" @click="await addRelays()">Add Relays</button>
<button class="button" @click="noThanks">No Thanks</button>
</div>
<div class="help">
<button class="button p-1.5" @click="await openOptions()">Settings</button>
</div>
<div class="disclaimer">
No user data is collected or transmitted.
All private keys are stored in the extension's sequestered local browser storage.
</div>
</body>
<div class="disclaimer">
No user data is collected or transmitted. All private keys are
stored in the extension's sequestered local browser storage.
</div>
</body>
</html> </html>

View File

@@ -9,59 +9,59 @@ export const RECOMMENDED_RELAYS = [
]; ];
// prettier-ignore // prettier-ignore
export const KINDS = [ export const KINDS = [
[0, 'Metadata', 'https://github.com/nostr-protocol/nips/blob/master/01.md'], [0, 'Metadata', 'https://github.com/nostr-protocol/nips/blob/master/01.md'],
[1, 'Text', 'https://github.com/nostr-protocol/nips/blob/master/01.md'], [1, 'Text', 'https://github.com/nostr-protocol/nips/blob/master/01.md'],
[2, 'Recommend Relay', 'https://github.com/nostr-protocol/nips/blob/master/01.md'], [2, 'Recommend Relay', 'https://github.com/nostr-protocol/nips/blob/master/01.md'],
[3, 'Contacts', 'https://github.com/nostr-protocol/nips/blob/master/02.md'], [3, 'Contacts', 'https://github.com/nostr-protocol/nips/blob/master/02.md'],
[4, 'Encrypted Direct Messages', 'https://github.com/nostr-protocol/nips/blob/master/04.md'], [4, 'Encrypted Direct Messages', 'https://github.com/nostr-protocol/nips/blob/master/04.md'],
[5, 'Event Deletion', 'https://github.com/nostr-protocol/nips/blob/master/09.md'], [5, 'Event Deletion', 'https://github.com/nostr-protocol/nips/blob/master/09.md'],
[6, 'Repost', 'https://github.com/nostr-protocol/nips/blob/master/18.md'], [6, 'Repost', 'https://github.com/nostr-protocol/nips/blob/master/18.md'],
[7, 'Reaction', 'https://github.com/nostr-protocol/nips/blob/master/25.md'], [7, 'Reaction', 'https://github.com/nostr-protocol/nips/blob/master/25.md'],
[8, 'Badge Award', 'https://github.com/nostr-protocol/nips/blob/master/58.md'], [8, 'Badge Award', 'https://github.com/nostr-protocol/nips/blob/master/58.md'],
[16, 'Generic Repost', 'https://github.com/nostr-protocol/nips/blob/master/18.md'], [16, 'Generic Repost', 'https://github.com/nostr-protocol/nips/blob/master/18.md'],
[40, 'Channel Creation', 'https://github.com/nostr-protocol/nips/blob/master/28.md'], [40, 'Channel Creation', 'https://github.com/nostr-protocol/nips/blob/master/28.md'],
[41, 'Channel Metadata', 'https://github.com/nostr-protocol/nips/blob/master/28.md'], [41, 'Channel Metadata', 'https://github.com/nostr-protocol/nips/blob/master/28.md'],
[42, 'Channel Message', 'https://github.com/nostr-protocol/nips/blob/master/28.md'], [42, 'Channel Message', 'https://github.com/nostr-protocol/nips/blob/master/28.md'],
[43, 'Channel Hide Message', 'https://github.com/nostr-protocol/nips/blob/master/28.md'], [43, 'Channel Hide Message', 'https://github.com/nostr-protocol/nips/blob/master/28.md'],
[44, 'Channel Mute User', 'https://github.com/nostr-protocol/nips/blob/master/28.md'], [44, 'Channel Mute User', 'https://github.com/nostr-protocol/nips/blob/master/28.md'],
[1063, 'File Metadata', 'https://github.com/nostr-protocol/nips/blob/master/94.md'], [1063, 'File Metadata', 'https://github.com/nostr-protocol/nips/blob/master/94.md'],
[1311, 'Live Chat Message', 'https://github.com/nostr-protocol/nips/blob/master/53.md'], [1311, 'Live Chat Message', 'https://github.com/nostr-protocol/nips/blob/master/53.md'],
[1984, 'Reporting', 'https://github.com/nostr-protocol/nips/blob/master/56.md'], [1984, 'Reporting', 'https://github.com/nostr-protocol/nips/blob/master/56.md'],
[1985, 'Label', 'https://github.com/nostr-protocol/nips/blob/master/32.md'], [1985, 'Label', 'https://github.com/nostr-protocol/nips/blob/master/32.md'],
[4550, 'Community Post Approval', 'https://github.com/nostr-protocol/nips/blob/master/72.md'], [4550, 'Community Post Approval', 'https://github.com/nostr-protocol/nips/blob/master/72.md'],
[7000, 'Job Feedback', 'https://github.com/nostr-protocol/nips/blob/master/90.md'], [7000, 'Job Feedback', 'https://github.com/nostr-protocol/nips/blob/master/90.md'],
[9041, 'Zap Goal', 'https://github.com/nostr-protocol/nips/blob/master/75.md'], [9041, 'Zap Goal', 'https://github.com/nostr-protocol/nips/blob/master/75.md'],
[9734, 'Zap Request', 'https://github.com/nostr-protocol/nips/blob/master/57.md'], [9734, 'Zap Request', 'https://github.com/nostr-protocol/nips/blob/master/57.md'],
[9735, 'Zap', 'https://github.com/nostr-protocol/nips/blob/master/57.md'], [9735, 'Zap', 'https://github.com/nostr-protocol/nips/blob/master/57.md'],
[10000, 'Mute List', 'https://github.com/nostr-protocol/nips/blob/master/51.md'], [10000, 'Mute List', 'https://github.com/nostr-protocol/nips/blob/master/51.md'],
[10001, 'Pin List', 'https://github.com/nostr-protocol/nips/blob/master/51.md'], [10001, 'Pin List', 'https://github.com/nostr-protocol/nips/blob/master/51.md'],
[10002, 'Relay List Metadata', 'https://github.com/nostr-protocol/nips/blob/master/65.md'], [10002, 'Relay List Metadata', 'https://github.com/nostr-protocol/nips/blob/master/65.md'],
[13194, 'Wallet Info', 'https://github.com/nostr-protocol/nips/blob/master/47.md'], [13194, 'Wallet Info', 'https://github.com/nostr-protocol/nips/blob/master/47.md'],
[22242, 'Client Authentication', 'https://github.com/nostr-protocol/nips/blob/master/42.md'], [22242, 'Client Authentication', 'https://github.com/nostr-protocol/nips/blob/master/42.md'],
[23194, 'Wallet Request', 'https://github.com/nostr-protocol/nips/blob/master/47.md'], [23194, 'Wallet Request', 'https://github.com/nostr-protocol/nips/blob/master/47.md'],
[23195, 'Wallet Response', 'https://github.com/nostr-protocol/nips/blob/master/47.md'], [23195, 'Wallet Response', 'https://github.com/nostr-protocol/nips/blob/master/47.md'],
[24133, 'Nostr Connect', 'https://github.com/nostr-protocol/nips/blob/master/46.md'], [24133, 'Nostr Connect', 'https://github.com/nostr-protocol/nips/blob/master/46.md'],
[27235, 'HTTP Auth', 'https://github.com/nostr-protocol/nips/blob/master/98.md'], [27235, 'HTTP Auth', 'https://github.com/nostr-protocol/nips/blob/master/98.md'],
[30000, 'Categorized People List', 'https://github.com/nostr-protocol/nips/blob/master/51.md'], [30000, 'Categorized People List', 'https://github.com/nostr-protocol/nips/blob/master/51.md'],
[30001, 'Categorized Bookmark List', 'https://github.com/nostr-protocol/nips/blob/master/51.md'], [30001, 'Categorized Bookmark List', 'https://github.com/nostr-protocol/nips/blob/master/51.md'],
[30008, 'Profile Badges', 'https://github.com/nostr-protocol/nips/blob/master/58.md'], [30008, 'Profile Badges', 'https://github.com/nostr-protocol/nips/blob/master/58.md'],
[30009, 'Badge Definition', 'https://github.com/nostr-protocol/nips/blob/master/58.md'], [30009, 'Badge Definition', 'https://github.com/nostr-protocol/nips/blob/master/58.md'],
[30017, 'Create or update a stall', 'https://github.com/nostr-protocol/nips/blob/master/15.md'], [30017, 'Create or update a stall', 'https://github.com/nostr-protocol/nips/blob/master/15.md'],
[30018, 'Create or update a product', 'https://github.com/nostr-protocol/nips/blob/master/15.md'], [30018, 'Create or update a product', 'https://github.com/nostr-protocol/nips/blob/master/15.md'],
[30023, 'Long-Form Content', 'https://github.com/nostr-protocol/nips/blob/master/23.md'], [30023, 'Long-Form Content', 'https://github.com/nostr-protocol/nips/blob/master/23.md'],
[30024, 'Draft Long-form Content', 'https://github.com/nostr-protocol/nips/blob/master/23.md'], [30024, 'Draft Long-form Content', 'https://github.com/nostr-protocol/nips/blob/master/23.md'],
[30078, 'Application-specific Data', 'https://github.com/nostr-protocol/nips/blob/master/78.md'], [30078, 'Application-specific Data', 'https://github.com/nostr-protocol/nips/blob/master/78.md'],
[30311, 'Live Event', 'https://github.com/nostr-protocol/nips/blob/master/53.md'], [30311, 'Live Event', 'https://github.com/nostr-protocol/nips/blob/master/53.md'],
[30315, 'User Statuses', 'https://github.com/nostr-protocol/nips/blob/master/38.md'], [30315, 'User Statuses', 'https://github.com/nostr-protocol/nips/blob/master/38.md'],
[30402, 'Classified Listing', 'https://github.com/nostr-protocol/nips/blob/master/99.md'], [30402, 'Classified Listing', 'https://github.com/nostr-protocol/nips/blob/master/99.md'],
[30403, 'Draft Classified Listing', 'https://github.com/nostr-protocol/nips/blob/master/99.md'], [30403, 'Draft Classified Listing', 'https://github.com/nostr-protocol/nips/blob/master/99.md'],
[31922, 'Date-Based Calendar Event', 'https://github.com/nostr-protocol/nips/blob/master/52.md'], [31922, 'Date-Based Calendar Event', 'https://github.com/nostr-protocol/nips/blob/master/52.md'],
[31923, 'Time-Based Calendar Event', 'https://github.com/nostr-protocol/nips/blob/master/52.md'], [31923, 'Time-Based Calendar Event', 'https://github.com/nostr-protocol/nips/blob/master/52.md'],
[31924, 'Calendar', 'https://github.com/nostr-protocol/nips/blob/master/52.md'], [31924, 'Calendar', 'https://github.com/nostr-protocol/nips/blob/master/52.md'],
[31925, 'Calendar Event RSVP', 'https://github.com/nostr-protocol/nips/blob/master/52.md'], [31925, 'Calendar Event RSVP', 'https://github.com/nostr-protocol/nips/blob/master/52.md'],
[31989, 'Handler recommendation', 'https://github.com/nostr-protocol/nips/blob/master/89.md'], [31989, 'Handler recommendation', 'https://github.com/nostr-protocol/nips/blob/master/89.md'],
[31990, 'Handler information', 'https://github.com/nostr-protocol/nips/blob/master/89.md'], [31990, 'Handler information', 'https://github.com/nostr-protocol/nips/blob/master/89.md'],
[34550, 'Community Definition', 'https://github.com/nostr-protocol/nips/blob/master/72.md'], [34550, 'Community Definition', 'https://github.com/nostr-protocol/nips/blob/master/72.md'],
]; ];
export async function initialize() { export async function initialize() {
@@ -87,7 +87,6 @@ async function migrate(version, goal) {
if (version === 1) { if (version === 1) {
console.log('migrating to version 2.'); console.log('migrating to version 2.');
let profiles = await getProfiles(); let profiles = await getProfiles();
profiles.forEach(profile => (profile.delegate = false));
await storage.set({ profiles }); await storage.set({ profiles });
return version + 1; return version + 1;
} }
@@ -156,7 +155,6 @@ export async function generateProfile(name = 'Default') {
privKey: await generatePrivateKey(), privKey: await generatePrivateKey(),
hosts: {}, hosts: {},
relays: [], relays: [],
delegate: false,
relayReminder: true, relayReminder: true,
}; };
} }
@@ -293,13 +291,6 @@ export async function toggleRelayReminder() {
await storage.set({ profiles }); await storage.set({ profiles });
} }
export async function getDelegator(index) {
let profiles = await getProfiles();
let profile = profiles[index];
console.log(profile);
return [profile.delegate, profile.delegator];
}
export async function getNpub() { export async function getNpub() {
let index = await getProfileIndex(); let index = await getProfileIndex();
return await browser.runtime.sendMessage({ return await browser.runtime.sendMessage({

View File

@@ -1,59 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script defer src="delegation.build.js"></script>
<link rel="stylesheet" href="/options.build.css">
<title>Delegation Wizard</title>
</head>
<body x-data="delegated" class="text-fuchsia-900 p-3.5 lg:p-32">
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">New Delegated Profile</h1>
<p class="mt-6">
A delegated user, as laid out in the
<a href="https://github.com/nostr-protocol/nips/blob/master/26.md" @click.prevent="openNip($event)">NIP-26
specification</a>,
is a user that is authorized to sign events for a different user. Additional limits can be
put on the delegated account, such as time limits.
</p>
<p class="mt-3">
This may be useful if you wish to keep your main key stored safely offline, but still post as that user
with a throwaway key.
</p>
<div class="section">
<h2 class="section-header">Delegator</h2>
<p class="text-small italic">The secure account to be delegated.</p>
<div class="mt-3">
<label for="delegator">Delegator Private Key</label>
<br>
<input type="text" class="input" x-model="privKey" :class="validKeyClass" autocapitalize="off" autocomplete="off"
spellcheck="off">
<p class="text-xs italic">This is not stored, but only used to sign a token which is stored instead.</p>
</div>
<div class="mt-3">
<label for="duration">Delegated Duration</label>
<br>
<select x-model.number="duration" class="input">
<option value="1">1 day</option>
<option value="7">7 days</option>
<option value="30">30 days</option>
</select>
</div>
</div>
<div class="mt-6">
<button class="button" @click.prevent="goBack">Back</button>
<button class="button" @click.prevent="save" :disabled="!isKeyValid">Save</button>
</div>
</body>
</html>

View File

@@ -1,86 +0,0 @@
import Alpine from 'alpinejs';
import {
generateProfile,
getProfiles,
validateKey,
} from '../../utilities/utils';
import { getPublicKey, nip26, nip19 } from 'nostr-tools';
const storage = browser.storage.local;
Alpine.data('delegated', () => ({
privKey: '',
duration: 7,
profile: {},
async init() {
this.profile = await generateProfile('New Delegate');
this.profile.delegate = true;
},
openNip(event) {
browser.tabs.create({ url: event.target.href, active: true });
},
goBack() {
window.location = browser.runtime.getURL('options.html');
},
async save() {
let profiles = await getProfiles();
// We need to jankify this Alpine proxy object so it's in the right format
// when we save it to storage
let profile = JSON.parse(JSON.stringify(this.profile));
profile.delegator = getPublicKey(this.decodedPrivKey);
profile.delegation = this.getDelegation();
profiles.push(profile);
let profileIndex = profiles.length - 1;
await storage.set({ profiles, profileIndex });
window.location = `${browser.runtime.getURL(
'options.html'
)}?index=${profileIndex}`;
},
getDelegation() {
let pubkey = getPublicKey(this.profile.privKey);
let delegation = nip26.createDelegation(this.decodedPrivKey, {
pubkey,
until: this.until,
since: Math.round(Date.now() / 1000) - 1,
});
console.log(delegation);
return delegation;
},
// Properties
get isKeyValid() {
return validateKey(this.privKey);
},
get validKeyClass() {
return this.isKeyValid
? ''
: 'ring-2 ring-rose-500 focus:ring-2 focus:ring-rose-500 border-transparent focus:border-transparent';
},
get until() {
return Math.round(Date.now() / 1000) + 60 * 60 * 24 * this.duration;
},
get decodedPrivKey() {
if (!this.isKeyValid) {
return null;
}
if (this.privKey.startsWith('nsec')) {
return nip19.decode(this.privKey).data;
}
return this.privKey;
},
}));
Alpine.start();

View File

@@ -1,15 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
let watch =
process.argv[2] === 'watch'
? {
onRebuild(error, result) {
if (error) console.error('watch rebuild failed: ', error);
else console.log('watch rebuild succeeded: ', result);
},
}
: false;
require('esbuild') require('esbuild')
.build({ .build({
entryPoints: { entryPoints: {
@@ -22,8 +12,6 @@ require('esbuild')
'./Shared (Extension)/Resources/permission/permission.js', './Shared (Extension)/Resources/permission/permission.js',
'experimental/experimental.build': 'experimental/experimental.build':
'./Shared (Extension)/Resources/experimental/experimental.js', './Shared (Extension)/Resources/experimental/experimental.js',
'wizards/delegation/delegation.build':
'./Shared (Extension)/Resources/wizards/delegation/delegation.js',
'event_history/event_history.build': 'event_history/event_history.build':
'./Shared (Extension)/Resources/event_history/event_history.js', './Shared (Extension)/Resources/event_history/event_history.js',
}, },
@@ -31,6 +19,5 @@ require('esbuild')
sourcemap: 'inline', sourcemap: 'inline',
bundle: true, bundle: true,
// minify: true, // minify: true,
watch,
}) })
.catch(() => process.exit(1)); .catch(() => process.exit(1));

3396
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,33 @@
{ {
"name": "nostore", "name": "nostore",
"version": "1.2.0", "version": "1.2.0",
"description": "", "description": "",
"source": [ "source": [
"background.js", "background.js",
"content.js", "content.js",
"nostr.js", "nostr.js",
"popup.html" "popup.html"
], ],
"scripts": { "scripts": {
"build": "./build.js", "build": "tailwindcss -i './Shared (Extension)/Resources/options.css' -o './Shared (Extension)/Resources/options.build.css' && ./build.js",
"watch": "./build.js watch", "watch": "./build.js watch",
"watch-tailwind": "tailwindcss -i './Shared (Extension)/Resources/options.css' -o './Shared (Extension)/Resources/options.build.css' --watch", "watch-tailwind": "tailwindcss -i './Shared (Extension)/Resources/options.css' -o './Shared (Extension)/Resources/options.build.css' --watch",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"alpinejs": "^3.10.5", "@alpinejs/csp": "^3.14.1",
"async-mutex": "^0.4.0", "alpinejs": "^3.14.1",
"idb": "^7.1.1", "async-mutex": "^0.5.0",
"json-format-highlight": "^1.0.4", "idb": "^8.0.0",
"nostr-tools": "^1.3.0" "json-format-highlight": "^1.0.4",
}, "nostr-tools": "^2.7.2"
"devDependencies": { },
"@tailwindcss/forms": "^0.5.3", "devDependencies": {
"esbuild": "^0.16.17", "@tailwindcss/forms": "^0.5.9",
"prettier": "^2.8.3", "esbuild": "^0.23.1",
"tailwindcss": "^3.2.4" "prettier": "^3.3.3",
} "tailwindcss": "^3.4.12"
}
} }