272 lines
7.0 KiB
TypeScript
272 lines
7.0 KiB
TypeScript
import { Form, ActionPanel, Action, Detail, showToast, Toast } from "@raycast/api";
|
|
import { useState, useEffect } from "react";
|
|
import * as fs from "fs";
|
|
import * as path from "path";
|
|
import * as os from "os";
|
|
|
|
interface Config {
|
|
url: string;
|
|
link?: string;
|
|
cookie?: string;
|
|
header1Name?: string;
|
|
header1Value?: string;
|
|
header2Name?: string;
|
|
header2Value?: string;
|
|
}
|
|
|
|
const CONFIG_FILE = path.join(os.homedir(), ".thoughtful-config.json");
|
|
|
|
function loadConfig(): Config | null {
|
|
try {
|
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
const data = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
return JSON.parse(data);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error loading config:", error);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function saveConfig(config: Config): void {
|
|
try {
|
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
} catch (error) {
|
|
console.error("Error saving config:", error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export default function Command() {
|
|
const [config, setConfig] = useState<Config | null>(null);
|
|
const [showSetup, setShowSetup] = useState(false);
|
|
const [response, setResponse] = useState<string | null>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const loadedConfig = loadConfig();
|
|
setConfig(loadedConfig);
|
|
if (!loadedConfig || !loadedConfig.link) {
|
|
setShowSetup(true);
|
|
}
|
|
}, []);
|
|
|
|
async function handleSetup(values: {
|
|
url: string;
|
|
link: string;
|
|
cookie: string;
|
|
header1Name: string;
|
|
header1Value: string;
|
|
header2Name: string;
|
|
header2Value: string;
|
|
}) {
|
|
if (!values.url.trim()) {
|
|
showToast({
|
|
style: Toast.Style.Failure,
|
|
title: "URL is required",
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
new URL(values.url);
|
|
} catch {
|
|
showToast({
|
|
style: Toast.Style.Failure,
|
|
title: "Invalid URL",
|
|
message: "Please enter a valid URL",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const newConfig: Config = {
|
|
url: values.url,
|
|
link: values.link || undefined,
|
|
cookie: values.cookie || undefined,
|
|
header1Name: values.header1Name || undefined,
|
|
header1Value: values.header1Value || undefined,
|
|
header2Name: values.header2Name || undefined,
|
|
header2Value: values.header2Value || undefined,
|
|
};
|
|
|
|
try {
|
|
saveConfig(newConfig);
|
|
setConfig(newConfig);
|
|
setShowSetup(false);
|
|
showToast({
|
|
style: Toast.Style.Success,
|
|
title: "Configuration saved",
|
|
});
|
|
} catch (error) {
|
|
showToast({
|
|
style: Toast.Style.Failure,
|
|
title: "Error saving configuration",
|
|
message: error instanceof Error ? error.message : String(error),
|
|
});
|
|
}
|
|
}
|
|
|
|
async function handleSubmit(values: { title: string; description: string }) {
|
|
if (!values.title.trim() || !values.description.trim()) {
|
|
showToast({
|
|
style: Toast.Style.Failure,
|
|
title: "Please enter both title and description",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!config) {
|
|
showToast({
|
|
style: Toast.Style.Failure,
|
|
title: "Configuration missing",
|
|
});
|
|
setShowSetup(true);
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
try {
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": "application/json",
|
|
};
|
|
|
|
if (config.cookie) {
|
|
headers["Cookie"] = config.cookie;
|
|
}
|
|
|
|
if (config.header1Name && config.header1Value) {
|
|
headers[config.header1Name] = config.header1Value;
|
|
}
|
|
if (config.header2Name && config.header2Value) {
|
|
headers[config.header2Name] = config.header2Value;
|
|
}
|
|
|
|
const res = await fetch(config.url, {
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify({ title: values.title, description: values.description }),
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`HTTP error! status: ${res.status}`);
|
|
}
|
|
|
|
const data = await res.text();
|
|
const linkUrl = config.link || config.url;
|
|
const responseWithLink = `${data}\n\n---\n\n[View Result](${linkUrl})`;
|
|
setResponse(responseWithLink);
|
|
showToast({
|
|
style: Toast.Style.Success,
|
|
title: "Response received",
|
|
});
|
|
} catch (error) {
|
|
console.error("Error making request:", error);
|
|
showToast({
|
|
style: Toast.Style.Failure,
|
|
title: "Error making request",
|
|
message: error instanceof Error ? error.message : String(error),
|
|
});
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
|
|
if (showSetup || !config) {
|
|
return (
|
|
<Form
|
|
actions={
|
|
<ActionPanel>
|
|
<Action.SubmitForm title="Save Configuration" onSubmit={handleSetup} />
|
|
{config && <Action title="Cancel" onAction={() => setShowSetup(false)} />}
|
|
</ActionPanel>
|
|
}
|
|
>
|
|
<Form.TextField
|
|
id="url"
|
|
title="API URL"
|
|
placeholder="https://api.example.com/endpoint"
|
|
defaultValue={config?.url}
|
|
autoFocus
|
|
/>
|
|
<Form.TextField
|
|
id="link"
|
|
title="Result Link URL"
|
|
placeholder="https://example.com/view"
|
|
defaultValue={config?.link}
|
|
/>
|
|
<Form.TextField
|
|
id="cookie"
|
|
title="Cookie Header"
|
|
placeholder="session=value; another=value (optional)"
|
|
defaultValue={config?.cookie}
|
|
/>
|
|
<Form.Separator />
|
|
<Form.TextField
|
|
id="header1Name"
|
|
title="Header 1 Name"
|
|
placeholder="Authorization (optional)"
|
|
defaultValue={config?.header1Name}
|
|
/>
|
|
<Form.TextField
|
|
id="header1Value"
|
|
title="Header 1 Value"
|
|
placeholder="Bearer token (optional)"
|
|
defaultValue={config?.header1Value}
|
|
/>
|
|
<Form.Separator />
|
|
<Form.TextField
|
|
id="header2Name"
|
|
title="Header 2 Name"
|
|
placeholder="X-Custom-Header (optional)"
|
|
defaultValue={config?.header2Name}
|
|
/>
|
|
<Form.TextField
|
|
id="header2Value"
|
|
title="Header 2 Value"
|
|
placeholder="value (optional)"
|
|
defaultValue={config?.header2Value}
|
|
/>
|
|
</Form>
|
|
);
|
|
}
|
|
|
|
if (response) {
|
|
return (
|
|
<Detail
|
|
markdown=""
|
|
actions={
|
|
<ActionPanel>
|
|
<Action title="Submit Another Input" onAction={() => setResponse(null)} />
|
|
<Action title="Change Configuration" onAction={() => setShowSetup(true)} />
|
|
</ActionPanel>
|
|
}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Form
|
|
isLoading={isLoading}
|
|
actions={
|
|
<ActionPanel>
|
|
<Action.SubmitForm title="Submit" onSubmit={handleSubmit} />
|
|
<Action title="Configure" onAction={() => setShowSetup(true)} />
|
|
</ActionPanel>
|
|
}
|
|
>
|
|
<Form.TextField
|
|
id="title"
|
|
title="Title"
|
|
placeholder="Enter title..."
|
|
autoFocus
|
|
/>
|
|
<Form.TextArea
|
|
id="description"
|
|
title="Description"
|
|
placeholder="Enter description..."
|
|
enableMarkdown
|
|
/>
|
|
</Form>
|
|
);
|
|
}
|