mirror of
https://github.com/joel-st/kunkun-nostr-goto-repo.git
synced 2025-06-19 00:25:03 +00:00
action to toggle open with x or y
This commit is contained in:
parent
b6da438a7d
commit
3aea45b6c5
10
dist/index-nip.js
vendored
10
dist/index-nip.js
vendored
File diff suppressed because one or more lines are too long
@ -1,190 +0,0 @@
|
|||||||
import { render, Fragment } from "preact"
|
|
||||||
import { useEffect, useRef, useState } from "preact/hooks"
|
|
||||||
import { ui, open } from "@kksh/api/ui/custom"
|
|
||||||
import { Button } from "@kksh/react"
|
|
||||||
import { Icon } from "@iconify/react";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList,
|
|
||||||
ThemeProvider
|
|
||||||
} from "@kksh/react"
|
|
||||||
|
|
||||||
import {
|
|
||||||
OpenInNewWindowIcon,
|
|
||||||
GitHubLogoIcon,
|
|
||||||
} from "@radix-ui/react-icons"
|
|
||||||
|
|
||||||
import "./index.css"
|
|
||||||
|
|
||||||
// Function to fetch NIPs from GitHub
|
|
||||||
async function fetchNostrNips() {
|
|
||||||
try {
|
|
||||||
// Fetch the README.md file from the repository
|
|
||||||
const response = await fetch('https://api.github.com/repos/nostr-protocol/nips/contents/README.md');
|
|
||||||
const fileData = await response.json();
|
|
||||||
|
|
||||||
// Decode content from base64
|
|
||||||
const content = atob(fileData.content);
|
|
||||||
|
|
||||||
// Regular expression to match NIP entries in the list
|
|
||||||
// Format is like: - [NIP-01: Basic protocol flow description](01.md)
|
|
||||||
const nipRegex = /\- \[NIP-(\d+)\: (.*?)\]\((\d+\.md)\)/g;
|
|
||||||
|
|
||||||
const nips = [];
|
|
||||||
let match;
|
|
||||||
|
|
||||||
// Find all matches in the content
|
|
||||||
while ((match = nipRegex.exec(content)) !== null) {
|
|
||||||
const nipNumber = match[1].padStart(2, '0'); // Pad to ensure consistent format like "01"
|
|
||||||
const nipNumberNoPad = parseInt(nipNumber); // remove leading zeros
|
|
||||||
const title = <Fragment>{match[2].split(/`([^`]+)`/).map((part, i) =>
|
|
||||||
i % 2 === 0 ? part : <code className="bg-gray-100 dark:bg-gray-800 rounded px-1.5 py-0.5" key={i}>{part}</code>
|
|
||||||
)}</Fragment>;
|
|
||||||
|
|
||||||
const filename = match[3];
|
|
||||||
|
|
||||||
nips.push({
|
|
||||||
nip: nipNumber,
|
|
||||||
title: title,
|
|
||||||
rawTitle: match[2],
|
|
||||||
urlGithub: `https://github.com/nostr-protocol/nips/blob/master/${filename}`,
|
|
||||||
urlNostrCom: `https://nips.nostr.com/${nipNumberNoPad}`,
|
|
||||||
// We're not fetching full content anymore, but we need to provide a placeholder
|
|
||||||
// for the filtering function that uses content
|
|
||||||
content: `NIP-${nipNumber}: ${title}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort NIPs by number
|
|
||||||
return nips.sort((a, b) => parseInt(a.nip) - parseInt(b.nip));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching NIPs:', error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const [input, setInput] = useState("")
|
|
||||||
const seachInputEle = useRef(null)
|
|
||||||
const [nips, setNips] = useState([])
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
ui.registerDragRegion()
|
|
||||||
ui.showMoveButton({
|
|
||||||
bottom: 0.2,
|
|
||||||
left: 0.2
|
|
||||||
})
|
|
||||||
|
|
||||||
// Fetch NIPs when component mounts
|
|
||||||
const loadNips = async () => {
|
|
||||||
setLoading(true)
|
|
||||||
try {
|
|
||||||
const fetchedNips = await fetchNostrNips()
|
|
||||||
setNips(fetchedNips)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load NIPs:', error)
|
|
||||||
} finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadNips()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
function onKeyDown(e) {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
if (input.length === 0) {
|
|
||||||
ui.goBack()
|
|
||||||
} else {
|
|
||||||
setInput("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter NIPs based on input
|
|
||||||
const filteredNips = input
|
|
||||||
? nips.filter(nip =>
|
|
||||||
nip.rawTitle.toLowerCase().includes(input.toLowerCase()) ||
|
|
||||||
nip.nip.includes(input) ||
|
|
||||||
nip.content.toLowerCase().includes(input.toLowerCase())
|
|
||||||
)
|
|
||||||
: nips
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeProvider>
|
|
||||||
<main className="h-screen">
|
|
||||||
<Command
|
|
||||||
onValueChange={(v) => {
|
|
||||||
setValue(v)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CommandInput
|
|
||||||
autoFocus
|
|
||||||
ref={seachInputEle}
|
|
||||||
placeholder="Search NIPs by number or title..."
|
|
||||||
style={{height: '3.25rem'}}
|
|
||||||
onInput={(e) => {
|
|
||||||
setInput(e.target.value)
|
|
||||||
}}
|
|
||||||
value={input}
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
>
|
|
||||||
<div className="h-8 w-8"></div>
|
|
||||||
</CommandInput>
|
|
||||||
<CommandList className="h-full">
|
|
||||||
{loading ? (
|
|
||||||
<div className="p-4 text-center">Loading NIPs...</div>
|
|
||||||
) : (
|
|
||||||
<Fragment>
|
|
||||||
<CommandEmpty>No NIPs found.</CommandEmpty>
|
|
||||||
<CommandGroup heading="Nostr Implementation Possibilities">
|
|
||||||
{filteredNips.map((nip) => (
|
|
||||||
<CommandItem
|
|
||||||
key={nip.nip}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between w-full">
|
|
||||||
<div className="truncate">
|
|
||||||
<span className="font-bold">NIP-{nip.nip}</span>: {nip.title}
|
|
||||||
</div>
|
|
||||||
<div className="flex space-x-2 ml-2">
|
|
||||||
<Button
|
|
||||||
style={{padding: '.1em .4em'}}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
open.url(nip.urlNostrCom);
|
|
||||||
}}
|
|
||||||
title="Open on nips.nostr.com"
|
|
||||||
>
|
|
||||||
<Icon icon="game-icons:ostrich" width="20" height="20" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
style={{padding: '.1em .4em'}}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
open.url(nip.urlGithub);
|
|
||||||
}}
|
|
||||||
title="Open on GitHub"
|
|
||||||
>
|
|
||||||
<Icon icon="mdi:github" width="20" height="20" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</main>
|
|
||||||
</ThemeProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render(<App />, document.getElementById("root"))
|
|
186
src/index-nip.ts
186
src/index-nip.ts
@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
Action,
|
Action,
|
||||||
clipboard,
|
|
||||||
expose,
|
expose,
|
||||||
Icon,
|
Icon,
|
||||||
IconEnum,
|
IconEnum,
|
||||||
@ -9,6 +8,7 @@ import {
|
|||||||
TemplateUiCommand,
|
TemplateUiCommand,
|
||||||
toast,
|
toast,
|
||||||
ui,
|
ui,
|
||||||
|
db
|
||||||
} from "@kksh/api/ui/template";
|
} from "@kksh/api/ui/template";
|
||||||
|
|
||||||
interface Nip {
|
interface Nip {
|
||||||
@ -22,6 +22,7 @@ interface Nip {
|
|||||||
|
|
||||||
class NostrOpenSpecificNip extends TemplateUiCommand {
|
class NostrOpenSpecificNip extends TemplateUiCommand {
|
||||||
private nips: Nip[] = [];
|
private nips: Nip[] = [];
|
||||||
|
private preferences: string = "";
|
||||||
private loading: boolean = false;
|
private loading: boolean = false;
|
||||||
private searchQuery: string = "";
|
private searchQuery: string = "";
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ class NostrOpenSpecificNip extends TemplateUiCommand {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.updateUI();
|
this.updateUI();
|
||||||
try {
|
try {
|
||||||
|
// Fetch NIPs
|
||||||
this.nips = await this.fetchNostrNips();
|
this.nips = await this.fetchNostrNips();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load NIPs:', error);
|
console.error('Failed to load NIPs:', error);
|
||||||
@ -36,31 +38,199 @@ class NostrOpenSpecificNip extends TemplateUiCommand {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.updateUI();
|
this.updateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await db.retrieveAll({
|
||||||
|
fields: ["data", "search_text"],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.length > 0 && data[0].data) {
|
||||||
|
this.preferences = JSON.parse(data[0].data) ? JSON.parse(data[0].data) : 'nostr';
|
||||||
|
this.updateUI();
|
||||||
|
} else {
|
||||||
|
this.preferences = 'nostr';
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load preferences:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to fetch NIPs from GitHub
|
// Function to fetch NIPs from GitHub
|
||||||
async fetchNostrNips(): Promise<Nip[]> {
|
async fetchNostrNips(): Promise<Nip[]> {
|
||||||
|
try {
|
||||||
|
// Fetch the README.md file from the repository
|
||||||
|
const response = await fetch('https://api.github.com/repos/nostr-protocol/nips/contents/README.md');
|
||||||
|
const fileData = await response.json();
|
||||||
|
|
||||||
|
// Decode content from base64
|
||||||
|
const content = atob(fileData.content);
|
||||||
|
|
||||||
|
// Regular expression to match NIP entries in the list
|
||||||
|
// Format is like: - [NIP-01: Basic protocol flow description](01.md)
|
||||||
|
const nipRegex = /\- \[NIP-(\d+)\: (.*?)\]\((\d+\.md)\)/g;
|
||||||
|
|
||||||
|
const nips: Nip[] = [];
|
||||||
|
let match;
|
||||||
|
|
||||||
|
// Find all matches in the content
|
||||||
|
while ((match = nipRegex.exec(content)) !== null) {
|
||||||
|
const nipNumber = match[1].padStart(2, '0'); // Pad to ensure consistent format like "01"
|
||||||
|
const nipNumberNoPad = parseInt(nipNumber); // remove leading zeros
|
||||||
|
const title = match[2];
|
||||||
|
const filename = match[3];
|
||||||
|
|
||||||
|
nips.push({
|
||||||
|
nip: nipNumber,
|
||||||
|
title: title,
|
||||||
|
rawTitle: title, // Store raw title without formatting
|
||||||
|
urlGithub: `https://github.com/nostr-protocol/nips/blob/master/${filename}`,
|
||||||
|
urlNostrCom: `https://nips.nostr.com/${nipNumberNoPad}`,
|
||||||
|
// We include content for filtering
|
||||||
|
content: `NIP-${nipNumber}: ${title}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort NIPs by number
|
||||||
|
return nips.sort((a, b) => parseInt(a.nip) - parseInt(b.nip));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching NIPs:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Filter NIPs based on search query
|
// Filter NIPs based on search query
|
||||||
getFilteredNips(): Nip[] {
|
getFilteredNips(): Nip[] {
|
||||||
return [];
|
if (!this.searchQuery) {
|
||||||
|
return this.nips;
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = this.searchQuery.toLowerCase();
|
||||||
|
return this.nips.filter(nip =>
|
||||||
|
nip.rawTitle.toLowerCase().includes(query) ||
|
||||||
|
nip.nip.includes(query) ||
|
||||||
|
nip.content.toLowerCase().includes(query)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle search input change
|
// Handle search input change
|
||||||
handleSearchInput(query: string): void {
|
async onSearchTermChange(query: string): Promise<void> {
|
||||||
this.searchQuery = query;
|
this.searchQuery = query;
|
||||||
this.updateUI();
|
this.updateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open URL in browser
|
// Create action panel for the footer
|
||||||
handleOpenUrl(url: string): void {
|
getFooterActions(): Action[] {
|
||||||
open.url(url);
|
let actions = [
|
||||||
|
new Action.Action({
|
||||||
|
title: "Always open on nips.nostr.com",
|
||||||
|
value: "open-nostr",
|
||||||
|
icon: new Icon({
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "game-icons:ostrich",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
new Action.Action({
|
||||||
|
title: "Always open on GitHub",
|
||||||
|
value: "open-github",
|
||||||
|
icon: new Icon({
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "mdi:github",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this.preferences === "github") {
|
||||||
|
actions.reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUI() {
|
return actions;
|
||||||
return 'Nostr NIPs';
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async updateUI() {
|
||||||
|
console.log('updateUI', this.preferences);
|
||||||
|
return ui
|
||||||
|
.setSearchBarPlaceholder("Search NIPs by number or title… or k[X] 🥳")
|
||||||
|
.then(() => {
|
||||||
|
const filteredNips = this.getFilteredNips();
|
||||||
|
|
||||||
|
if (this.loading) {
|
||||||
|
return ui.render(new List.List({
|
||||||
|
sections: [
|
||||||
|
new List.Section({
|
||||||
|
title: "Loading...",
|
||||||
|
items: []
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filteredNips.length === 0) {
|
||||||
|
return ui.render(new List.List({
|
||||||
|
sections: [
|
||||||
|
new List.Section({
|
||||||
|
title: "No NIPs found",
|
||||||
|
items: []
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ui.render(
|
||||||
|
new List.List({
|
||||||
|
sections: [
|
||||||
|
new List.Section({
|
||||||
|
title: "Nostr Implementation Possibilities",
|
||||||
|
items: filteredNips.map(
|
||||||
|
(nip) =>
|
||||||
|
new List.Item({
|
||||||
|
title: `NIP-${nip.nip}: ${nip.title}`,
|
||||||
|
value: this.preferences === "nostr" ? nip.urlNostrCom : nip.urlGithub,
|
||||||
|
icon: new Icon({
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "majesticons:open",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
actions: new Action.ActionPanel({
|
||||||
|
items: this.getFooterActions(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onListItemSelected(value: string): Promise<void> {
|
||||||
|
return open.url(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onActionSelected(value: string): Promise<void> {
|
||||||
|
if (value === "open-nostr") {
|
||||||
|
await db.deleteAll();
|
||||||
|
await db.add({
|
||||||
|
data: JSON.stringify('nostr'),
|
||||||
|
dataType: "preference",
|
||||||
|
searchText: "open_with",
|
||||||
|
});
|
||||||
|
this.preferences = 'nostr';
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === "open-github") {
|
||||||
|
await db.deleteAll();
|
||||||
|
await db.add({
|
||||||
|
data: JSON.stringify('github'),
|
||||||
|
dataType: "preference",
|
||||||
|
searchText: "open_with",
|
||||||
|
});
|
||||||
|
this.preferences = 'github';
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user