diff --git a/package-lock.json b/package-lock.json index 8b2ff39..85a87de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,10 @@ "dependencies": { "@raycast/api": "^1.104.9", "@raycast/utils": "^2.2.2", - "js-yaml": "^4.1.0" + "yaml": "^2.8.2" }, "devDependencies": { "@raycast/eslint-config": "^2.0.4", - "@types/js-yaml": "^4.0.9", "@types/node": "22.13.10", "@types/react": "19.0.10", "eslint": "^9.22.0", @@ -1582,13 +1581,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2218,6 +2210,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/async": { @@ -3495,6 +3488,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4663,7 +4657,6 @@ "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 817a371..069dbc3 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,10 @@ "dependencies": { "@raycast/api": "^1.104.9", "@raycast/utils": "^2.2.2", - "js-yaml": "^4.1.0" + "yaml": "^2.8.2" }, "devDependencies": { "@raycast/eslint-config": "^2.0.4", - "@types/js-yaml": "^4.0.9", "@types/node": "22.13.10", "@types/react": "19.0.10", "eslint": "^9.22.0", diff --git a/src/search-bookmarks.tsx b/src/search-bookmarks.tsx index 46712ff..7ff1272 100644 --- a/src/search-bookmarks.tsx +++ b/src/search-bookmarks.tsx @@ -1,9 +1,20 @@ import fs from "fs"; import os from "os"; +import { useState } from "react"; -import { Action, ActionPanel, getPreferenceValues, List, showToast, Toast } from "@raycast/api"; +import { + Action, + ActionPanel, + Form, + getPreferenceValues, + Icon, + List, + showToast, + Toast, + useNavigation, +} from "@raycast/api"; import { getFavicon } from "@raycast/utils"; -import yaml from "js-yaml"; +import YAML from "yaml"; interface Bookmark { title: string; @@ -21,11 +32,15 @@ function expandPath(filePath: string): string { return filePath; } +function readConfigFile(configPath: string): string { + const resolvedPath = expandPath(configPath); + return fs.readFileSync(resolvedPath, "utf-8"); +} + function loadBookmarks(configPath: string): { bookmarks: Bookmark[]; error?: string } { try { - const resolvedPath = expandPath(configPath); - const content = fs.readFileSync(resolvedPath, "utf-8"); - const config = yaml.load(content) as Config; + const content = readConfigFile(configPath); + const config = YAML.parse(content) as Config; if (!config || !Array.isArray(config.bookmarks)) { return { bookmarks: [], error: "Invalid config: 'bookmarks' array not found" }; @@ -39,14 +54,97 @@ function loadBookmarks(configPath: string): { bookmarks: Bookmark[]; error?: str } } +function saveBookmarkEdit( + configPath: string, + oldBookmark: Bookmark, + newBookmark: Bookmark, +): { success: boolean; error?: string } { + try { + const resolvedPath = expandPath(configPath); + const content = readConfigFile(configPath); + const doc = YAML.parseDocument(content); + + const bookmarks = doc.get("bookmarks") as YAML.YAMLSeq; + if (!bookmarks || !YAML.isSeq(bookmarks)) { + return { success: false, error: "Invalid config: 'bookmarks' array not found" }; + } + + let found = false; + for (const item of bookmarks.items) { + if (!YAML.isMap(item)) continue; + const title = item.get("title"); + const url = item.get("url"); + if (title === oldBookmark.title && url === oldBookmark.url) { + item.set("title", newBookmark.title); + item.set("url", newBookmark.url); + found = true; + break; + } + } + + if (!found) { + return { success: false, error: "Could not find bookmark in config file" }; + } + + fs.writeFileSync(resolvedPath, doc.toString(), "utf-8"); + return { success: true }; + } catch (e) { + const message = e instanceof Error ? e.message : String(e); + return { success: false, error: message }; + } +} + +function EditBookmarkForm(props: { bookmark: Bookmark; configPath: string; onEdit: () => void }) { + const { pop } = useNavigation(); + const { bookmark, configPath, onEdit } = props; + + async function handleSubmit(values: { title: string; url: string }) { + if (!values.title.trim() || !values.url.trim()) { + await showToast({ style: Toast.Style.Failure, title: "Title and URL are required" }); + return; + } + + const result = saveBookmarkEdit(configPath, bookmark, { + title: values.title.trim(), + url: values.url.trim(), + }); + + if (result.success) { + await showToast({ style: Toast.Style.Success, title: "Bookmark updated" }); + onEdit(); + pop(); + } else { + await showToast({ style: Toast.Style.Failure, title: "Failed to update bookmark", message: result.error }); + } + } + + return ( +
+ + + } + > + + + + ); +} + export default function Command() { const { configPath } = getPreferenceValues(); - const { bookmarks, error } = loadBookmarks(configPath); + + const [{ bookmarks, error }, setState] = useState(() => loadBookmarks(configPath)); if (error) { void showToast({ style: Toast.Style.Failure, title: "Failed to load bookmarks", message: error }); } + function reloadBookmarks() { + setState(loadBookmarks(configPath)); + } + return ( + } + /> } />