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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,20 +1,39 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<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">
<script src="../Script.js" defer></script>
</head>
<body>
<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-mac state-unknown">You can turn on Nostores extension in Safari Extensions 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>
<link rel="stylesheet" href="../Style.css" />
<script src="../Script.js" defer></script>
</head>
<body>
<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-mac state-unknown">
You can turn on Nostores extension in Safari Extensions
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>

View File

@@ -2,13 +2,20 @@ function show(platform, enabled, useSettingsInsteadOfPreferences) {
document.body.classList.add(`platform-${platform}`);
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-off')[0].innerText = "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…";
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-off')[0].innerText =
'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-off`, !enabled);
} else {
@@ -18,7 +25,9 @@ function show(platform, enabled, useSettingsInsteadOfPreferences) {
}
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 {
generatePrivateKey,
getPublicKey,
signEvent,
nip04,
nip19,
nip26,
getEventHash,
generateSecretKey,
getPublicKey,
finalizeEvent,
} from 'nostr-tools';
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
import { Mutex } from 'async-mutex';
import {
getProfileIndex,
@@ -14,7 +13,6 @@ import {
getProfile,
getPermission,
setPermission,
feature,
} from './utilities/utils';
import { saveEvent } from './utilities/db';
@@ -40,15 +38,13 @@ browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
deny(message);
return Promise.resolve(true);
case 'generatePrivateKey':
return Promise.resolve(generatePrivateKey());
return Promise.resolve(generatePrivateKey_());
case 'savePrivateKey':
return savePrivateKey(message.payload);
case 'getNpub':
return getNpub(message.payload);
case 'getNsec':
return getNsec(message.payload);
case 'createDelegation':
return createDelegation(message.payload);
case 'calcPubKey':
return Promise.resolve(getPublicKey(message.payload));
case 'npubEncode':
@@ -63,7 +59,7 @@ browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
case 'nip04.decrypt':
case 'getRelays':
validations[uuid] = sendResponse;
setDelegation(message).then(() => ask(uuid, message));
ask(uuid, message);
setTimeout(() => {
prompt.release?.();
}, 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 }) {
await forceRelease(); // Clean up previous tab if it closed without cleaning itself up
prompt.release = await prompt.mutex.acquire();
@@ -178,35 +179,32 @@ async function savePrivateKey([index, privKey]) {
privKey = nip19.decode(privKey).data;
}
let profiles = await get('profiles');
profiles[index].privKey = privKey;
profiles[index].privKey = bytesToHex(privKey);
await storage.set({ profiles });
return true;
}
async function getNsec(index) {
let profile = await getProfile(index);
let nsec = nip19.nsecEncode(profile.privKey);
let nsec = nip19.nsecEncode(hexToBytes(profile.privKey));
return nsec;
}
async function getNpub(index) {
let profile = await getProfile(index);
let pubKey = getPublicKey(profile.privKey);
let pubKey = getPublicKey(hexToBytes(profile.privKey));
let npub = nip19.npubEncode(pubKey);
return npub;
}
async function getPrivKey() {
let profile = await currentProfile();
return profile.privKey;
return hexToBytes(profile.privKey);
}
async function getPubKey() {
let pi = await getProfileIndex();
let profile = await getProfile(pi);
if (profile.delegate) {
return profile.delegator;
}
let privKey = await getPrivKey();
let pubKey = getPublicKey(privKey);
return pubKey;
@@ -220,11 +218,8 @@ async function currentProfile() {
async function signEvent_(event, host) {
event = JSON.parse(JSON.stringify(event));
let privKey = await getPrivKey();
let pubKey = getPublicKey(privKey);
event.pubkey = pubKey;
event.id = getEventHash(event);
event.sig = signEvent(event, privKey);
let sk = await getPrivKey();
event = finalizeEvent(event, sk);
saveEvent({
event,
metadata: { host, signed_at: Math.round(Date.now() / 1000) },
@@ -253,42 +248,3 @@ async function getRelays() {
});
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">
<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>
<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>
<style>
label {
display: block;
}
</style>
</head>
<style>
label {
display: block;
}
</style>
</head>
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="eventLog">
<p>
<a href="/options.html" class="border-none hover:underline"
>← Back</a
>
</p>
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="eventLog">
<p>
<a href="/options.html" class="border-none hover:underline">← Back</a>
</p>
<h1 class="section-header">Event History</h1>
<h1 class="section-header">Event History</h1>
<div class="section">
<div class="section-header">Filters</div>
<div class="section">
<div class="section-header">Filters</div>
<div class="grid grid-cols-2 xl:grid-cols-4 gap-4">
<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>
<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>
<label for="sort">Order</label>
<select
id="sort"
x-model="sort"
class="input"
@change="reload"
>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</div>
<div>
<label for="sort">Order</label>
<select id="sort" x-model="sort" class="input" @change="reload">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</div>
<div>
<label for="max">Max</label>
<input
type="number"
x-model.number="max"
@input.debounce.750ms="reload"
class="input"
min="10"
/>
</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></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>
<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>
<label for="toCreatedAt">To</label>
<input
type="date"
id="toCreatedAt"
x-model="toCreatedAt"
class="input"
@change="reload"
/>
</div>
<div x-show="view === 'created_at'" x-cloak>
<label for="toCreatedAt">To</label>
<input type="date" id="toCreatedAt" x-model="toCreatedAt" class="input" @change="reload">
</div>
<div x-show="view === 'kind'" x-cloak>
<label for="kindShortcut">Quick Select</label>
<select
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>
<label for="kindShortcut">Quick Select</label>
<select 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>
<label for="fromKind">From</label>
<input
type="number"
id="fromKind"
x-model.number="fromKind"
class="input"
@change="reload"
/>
</div>
<div x-show="view === 'kind'" x-cloak>
<label for="fromKind">From</label>
<input type="number" id="fromKind" x-model.number="fromKind" class="input" @change="reload">
</div>
<div x-show="view === 'kind'" x-cloak>
<label for="toKind">To</label>
<input
type="number"
id="toKind"
x-model.number="toKind"
class="input"
@change="reload"
/>
</div>
<div x-show="view === 'kind'" x-cloak>
<label for="toKind">To</label>
<input type="number" id="toKind" x-model.number="toKind" class="input" @change="reload">
</div>
<div x-show="view === 'host'" x-cloak>
<label for="host">Host</label>
<select
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>
<label for="host">Host</label>
<select 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 === 'pubkey'" x-cloak>
<label for="profiles">Profiles</label>
<select
id="profiles"
class="input"
x-model="profile"
@change="pkFromProfile"
>
<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>
<label for="profiles">Profiles</label>
<select id="profiles" class="input" x-model="profile" @change="pkFromProfile">
<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>
<label for="pubkey">Pubkey</label>
<input
type="text"
class="input"
x-model="pubkey"
@input.debounce="reload"
/>
</div>
</div>
<div x-show="view === 'pubkey'" x-cloak>
<label for="pubkey">Pubkey</label>
<input type="text" class="input" x-model="pubkey" @input.debounce="reload">
</div>
</div>
<div>
<button class="button mt-3" @click="saveAll">Save all</button>
<button class="button mt-3" @click="deleteAll">
Delete all
</button>
</div>
</div>
<div>
<button class="button mt-3" @click="saveAll">Save all</button>
<button class="button mt-3" @click="deleteAll">Delete all</button>
</div>
</div>
<div class="text-sm italic mt-1 text-right">
Click/tap event body to copy the raw event.
</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">
<div
class="select-none flex cursor-pointer text-sm md:text-xl"
@click="selected = selected === index ? null : index"
>
<div
class="flex-none w-14 p-4 font-extrabold"
x-text="selected === index ? '-' : '+'"
></div>
<div
class="flex-1 w-64 p-4"
x-text="formatDate(event.metadata.signed_at)"
></div>
<div
class="flex-1 w-64 p-4"
x-text="event.metadata.host"
></div>
<div
class="flex-1 w-64 p-4"
x-text="formatKind(event.event.kind)"
></div>
</div>
<template x-for="(event, index) in events">
<div class="mt-3 border-solid border border-fuchsia-700 rounded-lg">
<div class="select-none flex cursor-pointer text-sm md:text-xl"
@click="selected = selected === index ? null : index">
<div class="flex-none w-14 p-4 font-extrabold" x-text="selected === index ? '-' : '+'"></div>
<div class="flex-1 w-64 p-4" x-text="formatDate(event.metadata.signed_at)"></div>
<div class="flex-1 w-64 p-4" x-text="event.metadata.host"></div>
<div class="flex-1 w-64 p-4" x-text="formatKind(event.event.kind)"></div>
</div>
<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>
<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>

View File

@@ -1,34 +1,44 @@
<!DOCTYPE html>
<!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" />
<link rel="stylesheet" href="/options.build.css" />
<script src="/experimental/experimental.build.js" defer></script>
<title>Experimental Features</title>
</head>
<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>
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="experimental">
<p>
<a href="/options.html" class="border-none hover:underline"
>← Back</a
>
</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>
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="experimental">
<p>
<a href="/options.html" class="border-none hover:underline">← Back</a>
</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>
</html>
<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>

View File

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

View File

@@ -17,12 +17,8 @@
},
"content_scripts": [
{
"js": [
"content.build.js"
],
"matches": [
"<all_urls>"
]
"js": ["content.build.js"],
"matches": ["<all_urls>"]
}
],
"action": {
@@ -39,10 +35,7 @@
"options_ui": {
"page": "options.html"
},
"permissions": [
"storage",
"clipboardWrite"
],
"permissions": ["storage", "clipboardWrite"],
"web_accessible_resources": [
{
"resources": [
@@ -51,25 +44,20 @@
"options.build.js",
"options.build.css",
"options.html",
"wizards/delegation/delegation.html",
"wizards/delegation/delegation.build.js",
"wizards/delegation/delegation.build.css",
"experimental/experimental.html",
"experimental/experimental.build.js",
"event_history/event_history.html",
"event_history/event_history.build.js"
],
"matches": [
"<all_urls>"
]
"matches": ["<all_urls>"]
}
],
"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": {
"safari": {
"strict_min_version": "15.4"
}
}
}
}

View File

@@ -2,46 +2,48 @@
@tailwind components;
@tailwind utilities;
[x-cloak] { display: none; }
[x-cloak] {
display: none;
}
@layer components {
.button {
/* Colors */
@apply bg-fuchsia-900 hover:bg-fuchsia-800 active:bg-fuchsia-700 text-fuchsia-200 disabled:bg-gray-200 disabled:text-black;
.button {
/* Colors */
@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 */
@apply rounded-lg p-1.5 md:w-24 min-w-fit text-center;
}
/* Sizing and padding */
@apply rounded-lg p-1.5 md:w-24 min-w-fit text-center;
}
.input {
/* Colors */
@apply bg-fuchsia-200 text-fuchsia-800 disabled:bg-gray-200 disabled:text-black focus:border-fuchsia-800;
.input {
/* Colors */
@apply bg-fuchsia-200 text-fuchsia-800 disabled:bg-gray-200 disabled:text-black focus:border-fuchsia-800;
/* Sizing and padding */
@apply rounded-lg p-1.5 lg:p-1.5 w-full md:w-64;
}
/* Sizing and padding */
@apply rounded-lg p-1.5 lg:p-1.5 w-full md:w-64;
}
.checkbox {
/* Colors */
@apply text-fuchsia-800 bg-fuchsia-200 rounded-full accent-fuchsia-200;
.checkbox {
/* Colors */
@apply text-fuchsia-800 bg-fuchsia-200 rounded-full accent-fuchsia-200;
/* Sizing and padding */
@apply w-4 h-4 lg:w-5 lg:h-5;
}
/* Sizing and padding */
@apply w-4 h-4 lg:w-5 lg:h-5;
}
.section {
@apply border-2 border-fuchsia-700 rounded-lg p-1 md:p-5 mt-6 shadow-md;
}
.section {
@apply border-2 border-fuchsia-700 rounded-lg p-1 md:p-5 mt-6 shadow-md;
}
.section-header {
@apply text-2xl lg:text-5xl font-bold;
}
.section-header {
@apply text-2xl lg:text-5xl font-bold;
}
.subsection-header {
@apply text-xl lg:text-4xl font-bold;
}
.subsection-header {
@apply text-xl lg:text-4xl font-bold;
}
a {
@apply border-2 border-dotted text-fuchsia-800 border-fuchsia-800 hover:border-transparent;
}
}
a {
@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">
<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>
<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>
<body x-data="options" class="text-fuchsia-900 p-3.5 lg:p-32">
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Settings</h1>
</head>
<body x-data="options" class="text-fuchsia-900 p-3.5 lg:p-32">
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Settings</h1>
<!-- PROFILES -->
<div class="mt-6">
<label for="profiles">Profile</label>
<br>
<select class="input" x-model.number="profileIndex" id="profiles">
<template x-for="(name, index) in profileNames" :key="index">
<option x-text="name" :value="index"></option>
</template>
</select>
<div class="block md:inline p-3 pl-0 md:p-0">
<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>
<!-- PROFILES -->
<div class="mt-6">
<label for="profiles">Profile</label>
<br />
<select class="input" x-model.number="profileIndex" id="profiles">
<template x-for="(name, index) in profileNames" :key="index">
<option x-text="name" :value="index"></option>
</template>
</select>
<div class="block md:inline p-3 pl-0 md:p-0">
<button class="button" @click.prevent="newProfile">New</button>
<button class="button" @click.prevent="deleteProfile">
Delete
</button>
</div>
</div>
<label for="delegator-pub-key">Delegator Public Key</label>
<br>
<input id="delegator-pub-key" x-model="delegator" class="input" autocapitalize="off" autocomplete="off"
spellcheck="off" :disabled="delegate">
</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">
<label for="priv-key">Private Key</label>
<br>
<input x-model="privKey" :type="visibilityClass" class="input" :class="validKeyClass" autocapitalize="off"
autocomplete="off" spellcheck="off" :disabled="delegate">
<a href="" @click.prevent="visible = !visible" x-text="(visible && 'Hide') || 'Show'" class="border-none"></a>
</div>
<div class="mt-3">
<label for="priv-key">Private Key</label>
<br />
<input
x-model="privKey"
:type="visibilityClass"
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">
<label for="pub-key">Public Key</label>
<br>
<input x-model="pubKey" type="text" class="input" disabled>
<a href="" class="border-none" @click.prevent="copyPubKey" x-text="copied ? 'Copied!' : 'Copy'"></a>
</div>
<div class="mt-3">
<label for="pub-key">Public Key</label>
<br />
<input
x-model="pubKey"
type="text"
class="input"
disabled
/>
<a
href=""
class="border-none"
@click.prevent="copyPubKey"
x-text="copied ? 'Copied!' : 'Copy'"
></a>
</div>
<div class="mt-3">
<button class="button" :disabled="!needsSave || !validKey" @click.prevent="saveProfile">Save</button>
</div>
</form>
<div class="mt-3">
<button
class="button"
: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 -->
<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>
<template x-if="!hasRelays">
<div class="mt-3">
There are no relays assigned to this profile.
</div>
</template>
<template x-if="!hasRelays">
<div class="mt-3">
There are no relays assigned to this profile.
</div>
</template>
<div class="mt-3" x-show="hasRecommendedRelays" x-cloak>
<select x-model="recommendedRelay" class="input">
<option value="" disabled selected>
Recommended Relays
</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>
<select x-model="recommendedRelay" class="input">
<option value="" disabled selected>Recommended Relays</option>
<template x-for="relay in recommendedRelays">
<option :value="relay" x-text="relay"></option>
</template>
</select>
</div>
<input
class="mt-3 input"
x-model="newRelay"
type="text"
@keyup.enter="await addRelay()"
placeholder="wss://..."
autocomplete="off"
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://..."
autocomplete="off" 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>
<!-- PERMISSIONS -->
<div class="section">
<h2 class="section-header">App Permissions</h2>
<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">
<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 -->
<div class="section">
<h2 class="section-header">App Permissions</h2>
<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>
<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-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>
<table
class="mt-3 text-xs md:text-base table-fixed"
x-show="hostPerms.length > 0"
x-cloak
>
<thead class="font-bold text-lg">
<td class="p-2 text-center">App Request</td>
<td class="p-2 text-center">Action</td>
</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>
<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>
<button class="button" @click.prevent="clearData">
Clear Data
</button>
</div>
<table class="mt-3 text-xs md:text-base table-fixed" x-show="hostPerms.length > 0" x-cloak>
<thead class="font-bold text-lg">
<td class="p-2 text-center">App Request</td>
<td class="p-2 text-center">Action</td>
</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>
</html>
<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>

View File

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

View File

@@ -1,43 +1,58 @@
<!DOCTYPE html>
<!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="permission.build.js"></script>
<link rel="stylesheet" href="/options.build.css" />
<title>Permission Requested</title>
</head>
<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>
<body x-data="permission">
<div class="text-center">
<h1 class="section-header mt-5 text-center">
App is requesting permission
</h1>
<p class="mt-6 text-center">
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>
<body x-data="permission">
<div class="text-center">
<h1 class="section-header mt-5 text-center">App is requesting permission</h1>
<p class="mt-6 text-center">
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>
<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>
</html>

View File

@@ -2,7 +2,6 @@
color-scheme: light dark;
}
body {
width: 300px;
padding: 15px;
@@ -24,4 +23,4 @@ body {
margin-top: 10px;
font-size: 50%;
color: green;
}
}

View File

@@ -1,55 +1,87 @@
<!DOCTYPE html>
<!doctype 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>
<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>
<body x-data="popup">
<div class="profiles">
<label for="profile">Active Profile</label>
<div class="profile-buttons flex flex-row gap-2">
<div class="grow">
<select x-model.number="profileIndex" name="profile" id="profile" class="input">
<template x-for="(prof, index) in profileNames" :key="index">
<option x-text="prof" :value="index"></option>
</template>
</select>
<body x-data="popup">
<div class="profiles">
<label for="profile">Active Profile</label>
<div class="profile-buttons flex flex-row gap-2">
<div class="grow">
<select
x-model.number="profileIndex"
name="profile"
id="profile"
class="input"
>
<template
x-for="(prof, index) in profileNames"
:key="index"
>
<option x-text="prof" :value="index"></option>
</template>
</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>
<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>
</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>
<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>
</html>
<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>

View File

@@ -9,59 +9,59 @@ export const RECOMMENDED_RELAYS = [
];
// prettier-ignore
export const KINDS = [
[0, 'Metadata', '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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[1984, 'Reporting', 'https://github.com/nostr-protocol/nips/blob/master/56.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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[34550, 'Community Definition', 'https://github.com/nostr-protocol/nips/blob/master/72.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'],
[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'],
[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'],
[6, 'Repost', 'https://github.com/nostr-protocol/nips/blob/master/18.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'],
[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'],
[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'],
[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'],
[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'],
[1984, 'Reporting', 'https://github.com/nostr-protocol/nips/blob/master/56.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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[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'],
[34550, 'Community Definition', 'https://github.com/nostr-protocol/nips/blob/master/72.md'],
];
export async function initialize() {
@@ -87,7 +87,6 @@ async function migrate(version, goal) {
if (version === 1) {
console.log('migrating to version 2.');
let profiles = await getProfiles();
profiles.forEach(profile => (profile.delegate = false));
await storage.set({ profiles });
return version + 1;
}
@@ -156,7 +155,6 @@ export async function generateProfile(name = 'Default') {
privKey: await generatePrivateKey(),
hosts: {},
relays: [],
delegate: false,
relayReminder: true,
};
}
@@ -293,13 +291,6 @@ export async function toggleRelayReminder() {
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() {
let index = await getProfileIndex();
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
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')
.build({
entryPoints: {
@@ -22,8 +12,6 @@ require('esbuild')
'./Shared (Extension)/Resources/permission/permission.js',
'experimental/experimental.build':
'./Shared (Extension)/Resources/experimental/experimental.js',
'wizards/delegation/delegation.build':
'./Shared (Extension)/Resources/wizards/delegation/delegation.js',
'event_history/event_history.build':
'./Shared (Extension)/Resources/event_history/event_history.js',
},
@@ -31,6 +19,5 @@ require('esbuild')
sourcemap: 'inline',
bundle: true,
// minify: true,
watch,
})
.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",
"version": "1.2.0",
"description": "",
"source": [
"background.js",
"content.js",
"nostr.js",
"popup.html"
],
"scripts": {
"build": "./build.js",
"watch": "./build.js 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"
},
"author": "",
"license": "ISC",
"dependencies": {
"alpinejs": "^3.10.5",
"async-mutex": "^0.4.0",
"idb": "^7.1.1",
"json-format-highlight": "^1.0.4",
"nostr-tools": "^1.3.0"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.3",
"esbuild": "^0.16.17",
"prettier": "^2.8.3",
"tailwindcss": "^3.2.4"
}
}
"name": "nostore",
"version": "1.2.0",
"description": "",
"source": [
"background.js",
"content.js",
"nostr.js",
"popup.html"
],
"scripts": {
"build": "tailwindcss -i './Shared (Extension)/Resources/options.css' -o './Shared (Extension)/Resources/options.build.css' && ./build.js",
"watch": "./build.js 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"
},
"author": "",
"license": "ISC",
"dependencies": {
"@alpinejs/csp": "^3.14.1",
"alpinejs": "^3.14.1",
"async-mutex": "^0.5.0",
"idb": "^8.0.0",
"json-format-highlight": "^1.0.4",
"nostr-tools": "^2.7.2"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.9",
"esbuild": "^0.23.1",
"prettier": "^3.3.3",
"tailwindcss": "^3.4.12"
}
}