From 326f3aaa38927333f95626a86a9bd7a2d9efe831 Mon Sep 17 00:00:00 2001 From: Kamil Tomsik Date: Sat, 12 Oct 2024 22:27:54 +0200 Subject: [PATCH] remove unfinished & outdated things --- src/app/App.tsx | 4 - src/app/_components/TableInput.tsx | 117 --------------------------- src/app/_components/index.tsx | 1 - src/app/_util/index.ts | 1 - src/app/_util/parseHTML.ts | 26 ------ src/app/router.ts | 4 - src/app/workflow/Workflow.tsx | 124 ----------------------------- src/app/workflow/Workflows.tsx | 49 ------------ src/app/workflow/_examples.ts | 36 --------- src/app/workflow/runner.ts | 66 --------------- 10 files changed, 428 deletions(-) delete mode 100644 src/app/_components/TableInput.tsx delete mode 100644 src/app/_util/parseHTML.ts delete mode 100644 src/app/workflow/Workflow.tsx delete mode 100644 src/app/workflow/Workflows.tsx delete mode 100644 src/app/workflow/_examples.ts delete mode 100644 src/app/workflow/runner.ts diff --git a/src/app/App.tsx b/src/app/App.tsx index 5fad623..d9f6ad4 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -35,10 +35,6 @@ const Sidebar = () => ( Settings - - Twitter - Discord - diff --git a/src/app/_components/TableInput.tsx b/src/app/_components/TableInput.tsx deleted file mode 100644 index 33bbbfa..0000000 --- a/src/app/_components/TableInput.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { parseHTML, indexOf, slice } from "../_util" -import { Table } from "./Table" - -interface TableInputProps { - class?: string - value: string[][] - onChange?: (value: string[][]) => void -} - -/** - * Simple spreadsheet-like table input - */ -export const TableInput = ({ class: className = "", value }: TableInputProps) => { - return ( - - - {value.map(row => ( - - {row.map(cell => ( - - ))} - - ))} - -
- {cell} -
- ) -} - -const restoreFocus = e => (e.target.querySelector("[aria-selected=true]") ?? e.target.querySelector("td"))?.focus() - -const updateSelection = e => { - e.target - .closest("table")! - .querySelectorAll("[aria-selected=true]") - .forEach(td => ((td.tabIndex = -1), td.removeAttribute("aria-selected"))) - e.target.setAttribute("aria-selected", "true") - e.target.tabIndex = 0 -} - -const handleMouseDown = e => { - if (e.target instanceof HTMLTableCellElement) { - e.preventDefault() - window.getSelection()?.setPosition(e.target, 0) - } -} - -const handleKeyDown = e => { - if (e.ctrlKey || e.metaKey || e.altKey || e.key === "Tab" || e.key === "Shift") return - - const { td, tr, x } = findCell(e) - - // Move focus - if (e.key.startsWith("Arrow") || e.key === "Enter") { - // TODO: Handle selection - if (e.shiftKey) return - - e.preventDefault() - - switch (e.key) { - case "ArrowLeft": - return td.previousElementSibling?.focus() - case "ArrowRight": - return td.nextElementSibling?.focus() - case "ArrowUp": - return tr.previousElementSibling?.children[x]?.focus() - case "ArrowDown": - case "Enter": - return tr.nextElementSibling?.children[x]?.focus() - } - } - - // Delete cell content - const selection = window.getSelection() - if (selection?.anchorOffset === 0) { - selection.selectAllChildren(td) - } -} - -const findCell = (e: any) => { - const td = e.target.closest("td") - const tr = td.closest("tr") - const x = indexOf(tr.children, td) - const y = indexOf(tr.parentElement.children, tr) - - return { td, tr, x, y } -} - -const handlePaste = e => { - e.preventDefault() - - if (e.target instanceof HTMLTableCellElement) { - const html = e.clipboardData?.getData("text/html") - const text = e.clipboardData?.getData("text/plain") - const data = html - ? [...parseHTML(html).querySelectorAll("tr")].map(tr => [...tr.querySelectorAll("td")].map(td => td.textContent ?? "")) - : text.split("\n").map(r => r.split("\t")) - - if (data) { - const { tr, x, y } = findCell(e) - - slice(tr.parentElement.children, y, y + data.length).forEach((tr, i) => { - slice(tr.children, x, x + data[i].length).forEach((td, j) => { - td.textContent = data[i][j] - }) - }) - } - } -} diff --git a/src/app/_components/index.tsx b/src/app/_components/index.tsx index 851bf6b..369eef8 100644 --- a/src/app/_components/index.tsx +++ b/src/app/_components/index.tsx @@ -18,6 +18,5 @@ export { Resizable } from "./Resizable" export { SearchField } from "./SearchField" export { Select } from "./Select" export { Table } from "./Table" -export { TableInput } from "./TableInput" export { Tabs } from "./Tabs" export { Value } from "./Value" diff --git a/src/app/_util/index.ts b/src/app/_util/index.ts index 4716df6..4850209 100644 --- a/src/app/_util/index.ts +++ b/src/app/_util/index.ts @@ -3,5 +3,4 @@ export { err } from "./err" export { dedent, fmtCount, fmtSize, fmtDuration, fmtDate, humanize } from "./fmt" export { jsonLines } from "./jsonLines" export { basename } from "./path" -export { parseHTML } from "./parseHTML" export { template, parseVars } from "./template" diff --git a/src/app/_util/parseHTML.ts b/src/app/_util/parseHTML.ts deleted file mode 100644 index f746457..0000000 --- a/src/app/_util/parseHTML.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const parseHTML = (input: string, clean = false) => { - const doc = new DOMParser().parseFromString(input, "text/html") - - if (clean) { - // Strip out scripts, styles and javascript links - doc.querySelectorAll("script, link, style, a[href^='javascript:']").forEach(el => el.remove()) - - // Remove style attribute - doc.querySelectorAll("[style]").forEach(el => el.removeAttribute("style")) - - // Then for all elements (but in reverse) - Array.from(doc.querySelectorAll("*")) - .reverse() - .forEach(el => { - // Remove all data-* attributes - for (const k of el.getAttributeNames()) { - if (k.startsWith("data-")) el.removeAttribute(k) - } - - // Remove if empty - if (!el.innerHTML.trim()) el.remove() - }) - } - - return doc -} diff --git a/src/app/router.ts b/src/app/router.ts index 2cda620..2eb5106 100644 --- a/src/app/router.ts +++ b/src/app/router.ts @@ -5,8 +5,6 @@ import { QuickTools } from "./quick-tools/QuickTools" import { CreateTool } from "./quick-tools/CreateTool" import { QuickTool } from "./quick-tools/QuickTool" import { EditTool } from "./quick-tools/EditTool" -import { Workflows } from "./workflow/Workflows" -import { Workflow } from "./workflow/Workflow" import { SearchModels } from "./models/SearchModels" import { DownloadManager } from "./models/DownloadManager" import { Models } from "./models/Models" @@ -23,8 +21,6 @@ const routes = [ { path: "/quick-tools/new", component: CreateTool }, { path: "/quick-tools/:id", component: QuickTool }, { path: "/quick-tools/:id/edit", component: EditTool }, - NEXT && { path: "/workflows", component: Workflows }, - NEXT && { path: "/workflows/:id", component: Workflow }, { path: "/playground", component: Playground }, { path: "/models", component: SearchModels }, { path: "/models/downloads", component: DownloadManager }, diff --git a/src/app/workflow/Workflow.tsx b/src/app/workflow/Workflow.tsx deleted file mode 100644 index 79b729c..0000000 --- a/src/app/workflow/Workflow.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { createContext } from "preact" -import { useContext } from "preact/hooks" -import { useSignal } from "@preact/signals" -import { Clock, Cloud, SquareMousePointer, Play, Repeat2, TableProperties, Wand2 } from "lucide" -import { Field, Form, FormGrid, Icon, IconButton, Page } from "../_components" -import { err, fmtDuration, humanize } from "../_util" -import { examples } from "./_examples" -import { handlers, runWorkflow, stepDefaults } from "./runner" - -const SelectionContext = createContext({ selection: null, select: null } as any) - -export const Workflow = ({ params }) => { - const workflow = examples.find(w => w.id === +params.id) ?? err("Unknown workflow") // TODO: useApi() - - const ctx = { - selection: useSignal(null as any), - select: step => (ctx.selection.value = step), - } - - return ( - - - runWorkflow(workflow)} /> - - - - {!workflow.steps.length &&

Add steps from the right to build your workflow.

} - - - {workflow.steps.map(s => ( - - ))} - -
- - - - - -
- ) -} - -const Step = ({ step }) => { - const { selection, select } = useContext(SelectionContext) - const [[kind, props]] = Object.entries(step) - const [title, icon, subtitle] = renderers[kind](props) - - const selected = selection?.value === step - - return ( -
select(step)} - draggable - > -
- -
-
-

{title}

-
{subtitle}
-
-
- ) -} - -const PropsPane = ({ step }) => ( - <> -

- {step ? Object.keys(step)[0] : "No step selected"} -

- -
- {step ? ( -
console.log(data)} - onChange={data => Object.assign(step[Object.keys(step)[0]], data)} - > - - {Object.keys(step[Object.keys(step)[0]]).map(k => ( - <> - - - - ))} - -
- ) : ( -

Click on a step to edit its properties, or drag a step from the sidebar to add it to the workflow.

- )} -
- -) - -const StepCatalog = () => ( -
-

Step Catalog

- -
- {Object.keys(handlers).map(k => ( - - ))} -
-
-) - -type Renderers = { - [k in keyof typeof handlers]: (s: (typeof stepDefaults)[k]) => [string, typeof Clock, string?] -} - -const renderers: Renderers = { - wait: s => [`Wait ${fmtDuration(s.duration)}`, Clock], - generate: s => ["Generate", Wand2], - instruction: s => ["Instruction", Wand2, s.instruction], - http_request: s => ["HTTP Request", Cloud, s.url], - query_selector: s => ["Query Selector", SquareMousePointer, s.selector], - // extract: s => ["Extract", TableProperties, s.fields.join(", ")], - // for_each: s => ["For Each", Repeat2], -} diff --git a/src/app/workflow/Workflows.tsx b/src/app/workflow/Workflows.tsx deleted file mode 100644 index ad4a583..0000000 --- a/src/app/workflow/Workflows.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Plus } from "lucide" -import { Alert, IconButton, Link, Page, Table } from "../_components" -import { examples } from "./_examples" -import { router } from "../router" - -export const Workflows = () => { - const createWorkflow = () => { - const id = Date.now() - examples.push({ id, name: "New Workflow", steps: [] }) - router.navigate(`/workflows/${id}`) - } - - return ( - - - - - - - - This feature is experimental.
- No changes are saved to the database -
- - - - - - - - - - {examples.map(w => ( - - - - {/* TODO: Automatic when the first action is a trigger/cron */} - - ))} - -
NameKind
- - {w.name} - - Manual
-
-
- ) -} diff --git a/src/app/workflow/_examples.ts b/src/app/workflow/_examples.ts deleted file mode 100644 index a2f52aa..0000000 --- a/src/app/workflow/_examples.ts +++ /dev/null @@ -1,36 +0,0 @@ -export const examples = [ - { - id: 1, - name: "Chuck Norris Jokes Explained", - steps: [ - { http_request: { method: "GET", url: "https://api.chucknorris.io/jokes/random" } }, - { instruction: { instruction: "Extract the value part." } }, - { instruction: { instruction: "Explain the joke, reason step by step." } }, - ], - }, - - { - id: 2, - name: "Local Llama Top Posts", - steps: [ - { http_request: { method: "GET", url: "https://old.reddit.com/r/LocalLLaMA/top/" } }, - { query_selector: { selector: ".thing" } }, - { instruction: { instruction: "Extract the title and url and respond with valid JSON." } }, - ], - }, - - { - id: 3, - name: "Scrape Hacker News Jobs", - steps: [ - { wait: { duration: 2 } }, - { http_request: { method: "GET", url: "https://hn.svelte.dev/jobs/1" } }, - { query_selector: { selector: "main article", limit: 5 } }, - { - instruction: { - instruction: "Extract the job title, company and expected skills for the role. Respond with valid JSON.", - }, - }, - ], - }, -] diff --git a/src/app/workflow/runner.ts b/src/app/workflow/runner.ts deleted file mode 100644 index b19a221..0000000 --- a/src/app/workflow/runner.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { signal } from "@preact/signals" -import { generate } from "../_hooks/useGenerate" -import { parseHTML } from "../_util" - -export type Step = (typeof stepDefaults)[keyof typeof stepDefaults] - -// TODO: this should be retrieved from the server -export const stepDefaults = { - wait: { duration: 1 }, - generate: {}, // TODO: opts for sampling, etc. - instruction: { instruction: "" }, - http_request: { method: "GET", url: "" }, - query_selector: { selector: "", limit: 2, clean: true }, -} - -type Handlers = { - [k in keyof typeof stepDefaults]: (opts: (typeof stepDefaults)[k], input: string) => string | Promise -} - -// TODO: this should be implemented on the server side -export const handlers: Handlers = { - wait: ({ duration }) => { - return new Promise(resolve => setTimeout(resolve, duration * 1000)) - }, - - generate: (opts, input) => { - const result = signal("") - const status = signal(null) - - return generate({ ...opts, prompt: input }, result, status) - }, - - instruction: ({ instruction, ...opts }, input) => { - return handlers.generate(opts, `ASSISTANT: ${input}\n\nUSER: ${instruction}\n\nASSISTANT: Sure!`) - }, - - http_request: ({ url }) => { - return fetch("/api/proxy", { method: "POST", body: JSON.stringify({ url }) }).then(res => res.text()) - }, - - query_selector: ({ selector, limit = 2, clean = true }, input) => { - return [...parseHTML(input, clean).querySelectorAll(selector)] - .slice(0, limit) - .map(el => el.innerHTML) - .join("") - }, -} - -export const runWorkflow = async workflow => { - const runStep = async (step, input) => { - for (const k in handlers) { - if (k in step) { - const res = await handlers[k](step[k], input) - - return res - } - } - - throw new Error(`No handler found for step ${JSON.stringify(step)}`) - } - - let input = null - for (const step of workflow.steps) { - input = await runStep(step, input) - } -}