mirror of
https://github.com/joel-st/kunkun-nostr-goto-repo.git
synced 2025-06-18 16:15:02 +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"))
|
188
src/index-nip.ts
188
src/index-nip.ts
@ -1,6 +1,5 @@
|
||||
import {
|
||||
Action,
|
||||
clipboard,
|
||||
expose,
|
||||
Icon,
|
||||
IconEnum,
|
||||
@ -9,6 +8,7 @@ import {
|
||||
TemplateUiCommand,
|
||||
toast,
|
||||
ui,
|
||||
db
|
||||
} from "@kksh/api/ui/template";
|
||||
|
||||
interface Nip {
|
||||
@ -22,6 +22,7 @@ interface Nip {
|
||||
|
||||
class NostrOpenSpecificNip extends TemplateUiCommand {
|
||||
private nips: Nip[] = [];
|
||||
private preferences: string = "";
|
||||
private loading: boolean = false;
|
||||
private searchQuery: string = "";
|
||||
|
||||
@ -29,6 +30,7 @@ class NostrOpenSpecificNip extends TemplateUiCommand {
|
||||
this.loading = true;
|
||||
this.updateUI();
|
||||
try {
|
||||
// Fetch NIPs
|
||||
this.nips = await this.fetchNostrNips();
|
||||
} catch (error) {
|
||||
console.error('Failed to load NIPs:', error);
|
||||
@ -36,31 +38,199 @@ class NostrOpenSpecificNip extends TemplateUiCommand {
|
||||
this.loading = false;
|
||||
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
|
||||
async fetchNostrNips(): Promise<Nip[]> {
|
||||
return [];
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
|
||||
// Filter NIPs based on search query
|
||||
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
|
||||
handleSearchInput(query: string): void {
|
||||
async onSearchTermChange(query: string): Promise<void> {
|
||||
this.searchQuery = query;
|
||||
this.updateUI();
|
||||
}
|
||||
|
||||
// Open URL in browser
|
||||
handleOpenUrl(url: string): void {
|
||||
open.url(url);
|
||||
// Create action panel for the footer
|
||||
getFooterActions(): Action[] {
|
||||
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()
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
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