From 05cbf2bc64e5fced7e5067c31c510722029d04e7 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 7 Oct 2025 14:13:56 -0700 Subject: [PATCH 01/20] add section --- .../docs/docs/user-guide/workflow-setup.md | 4 ++ frontend/src/components/ui/search-combobox.ts | 10 ++- .../crawl-workflows/workflow-editor.ts | 61 +++++++++++++++++++ .../src/strings/crawl-workflows/section.ts | 1 + frontend/src/types/crawler.ts | 1 + frontend/src/utils/workflow.ts | 9 +++ 6 files changed, 85 insertions(+), 1 deletion(-) diff --git a/frontend/docs/docs/user-guide/workflow-setup.md b/frontend/docs/docs/user-guide/workflow-setup.md index 3f86223c5f..ae76d7263a 100644 --- a/frontend/docs/docs/user-guide/workflow-setup.md +++ b/frontend/docs/docs/user-guide/workflow-setup.md @@ -392,6 +392,10 @@ You can use a tool like [crontab.guru](https://crontab.guru/) to check Cron synt Cron schedules are always in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). +## Deduplication + +Prevent duplicate content from being crawled and stored. + ## Collections ### Auto-Add to Collection diff --git a/frontend/src/components/ui/search-combobox.ts b/frontend/src/components/ui/search-combobox.ts index 6907612a27..d0c80398d7 100644 --- a/frontend/src/components/ui/search-combobox.ts +++ b/frontend/src/components/ui/search-combobox.ts @@ -3,6 +3,7 @@ import type { SlInput, SlMenuItem } from "@shoelace-style/shoelace"; import Fuse from "fuse.js"; import { html, LitElement, nothing, type PropertyValues } from "lit"; import { customElement, property, query, state } from "lit/decorators.js"; +import { ifDefined } from "lit/directives/if-defined.js"; import { when } from "lit/directives/when.js"; import debounce from "lodash/fp/debounce"; @@ -44,6 +45,12 @@ export class SearchCombobox extends LitElement { @property({ type: String }) searchByValue = ""; + @property({ type: String }) + label?: string; + + @property({ type: String }) + size: SlInput["size"] = "small"; + private get hasSearchStr() { return this.searchByValue.length >= MIN_SEARCH_LENGTH; } @@ -115,8 +122,9 @@ export class SearchCombobox extends LitElement { }} > { diff --git a/frontend/src/features/crawl-workflows/workflow-editor.ts b/frontend/src/features/crawl-workflows/workflow-editor.ts index cc3e629c63..af033be6da 100644 --- a/frontend/src/features/crawl-workflows/workflow-editor.ts +++ b/frontend/src/features/crawl-workflows/workflow-editor.ts @@ -205,6 +205,10 @@ const getDefaultProgressState = (hasConfigId = false): ProgressState => { error: false, completed: hasConfigId, }, + deduplication: { + error: false, + completed: hasConfigId, + }, collections: { error: false, completed: hasConfigId, @@ -387,6 +391,11 @@ export class WorkflowEditor extends BtrixElement { "": "", }; + private readonly dedupeTypeLabels: Record = { + collection: msg("Deduplicate using a collection"), + none: msg("No deduplication"), + }; + @query(`form[name="${formName}"]`) private readonly formElem?: HTMLFormElement; @@ -2284,6 +2293,53 @@ https://archiveweb.page/images/${"logo.svg"}`} `; }; + private renderDeduplication() { + return html` ${inputCol(html` + + this.updateFormState({ + dedupeType: (e.target as SlRadio).value as FormState["dedupeType"], + })} + > + ${this.dedupeTypeLabels["none"]} + ${this.dedupeTypeLabels["collection"]} + + `)} + ${this.renderHelpTextCol( + msg( + `Enable duplication checks before and during a crawl to avoid duplicate content in archived items.`, + ), + )} + ${when( + this.formState.dedupeType === "collection", + this.renderDedupeCollection, + )}`; + } + + private readonly renderDedupeCollection = () => { + return html` + ${this.renderSectionHeading(msg("Set Collection"))} + ${inputCol(html` + + + `)} + ${this.renderHelpTextCol( + msg( + "Compare crawls from this workflow with all archived items in a specific collection. Crawls of this workflow will be automatically added to the collection.", + ), + )} + `; + }; + private renderCollections() { return html` ${inputCol(html` @@ -2470,6 +2526,11 @@ https://archiveweb.page/images/${"logo.svg"}`} desc: msg("Schedule recurring crawls."), render: this.renderJobScheduling, }, + { + name: "deduplication", + desc: msg("Prevent duplicate content from being crawled and stored."), + render: this.renderDeduplication, + }, { name: "collections", desc: msg("Add crawls from this workflow to one or more collections."), diff --git a/frontend/src/strings/crawl-workflows/section.ts b/frontend/src/strings/crawl-workflows/section.ts index 1b09c14064..2e30505bae 100644 --- a/frontend/src/strings/crawl-workflows/section.ts +++ b/frontend/src/strings/crawl-workflows/section.ts @@ -8,6 +8,7 @@ const section: Record = { behaviors: msg("Page Behavior"), browserSettings: msg("Browser Settings"), scheduling: msg("Scheduling"), + deduplication: msg("Deduplication"), collections: msg("Collections"), metadata: msg("Metadata"), }; diff --git a/frontend/src/types/crawler.ts b/frontend/src/types/crawler.ts index 3794355138..931de351a4 100644 --- a/frontend/src/types/crawler.ts +++ b/frontend/src/types/crawler.ts @@ -70,6 +70,7 @@ export type WorkflowParams = { autoAddCollections: string[]; crawlerChannel: string; proxyId: string | null; + dedupCollId?: string; }; export type CrawlConfig = WorkflowParams & { diff --git a/frontend/src/utils/workflow.ts b/frontend/src/utils/workflow.ts index d6930f2fae..e0282e3caa 100644 --- a/frontend/src/utils/workflow.ts +++ b/frontend/src/utils/workflow.ts @@ -39,6 +39,7 @@ export const SECTIONS = [ "behaviors", "browserSettings", "scheduling", + "deduplication", "collections", "metadata", ] as const; @@ -51,6 +52,7 @@ export enum GuideHash { Behaviors = "page-behavior", BrowserSettings = "browser-settings", Scheduling = "scheduling", + Deduplication = "deduplication", Collections = "collections", Metadata = "metadata", } @@ -66,6 +68,7 @@ export const workflowTabToGuideHash: Record = { behaviors: GuideHash.Behaviors, browserSettings: GuideHash.BrowserSettings, scheduling: GuideHash.Scheduling, + deduplication: GuideHash.Deduplication, collections: GuideHash.Collections, metadata: GuideHash.Metadata, }; @@ -169,6 +172,7 @@ export type FormState = { * Custom schedule in cron format. */ scheduleCustom?: string; + dedupeType: "none" | "collection"; jobName: WorkflowParams["name"]; browserProfile: Profile | null; tags: Tags; @@ -231,6 +235,7 @@ export const getDefaultFormState = (): FormState => ({ minute: 0, period: "AM", }, + dedupeType: "collection", jobName: "", browserProfile: null, tags: [], @@ -335,6 +340,10 @@ export function getInitialFormState(params: { formState.autoAddCollections = params.initialWorkflow.autoAddCollections; } + if (params.initialWorkflow.dedupCollId) { + formState.dedupeType = "collection"; + } + const secondsToMinutes = (value: unknown, fallback = 0) => { if (typeof value === "number" && value > 0) return value / 60; return fallback; From 19efa653c62a493eab3cfd00e78b03b350b48948 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 7 Oct 2025 14:50:06 -0700 Subject: [PATCH 02/20] add context callback --- .../search-org/WithSearchOrgContext.ts | 14 +++++++++-- .../collections/choose-collection-name.ts | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 frontend/src/features/collections/choose-collection-name.ts diff --git a/frontend/src/context/search-org/WithSearchOrgContext.ts b/frontend/src/context/search-org/WithSearchOrgContext.ts index df09c942ae..eb6eadd87e 100644 --- a/frontend/src/context/search-org/WithSearchOrgContext.ts +++ b/frontend/src/context/search-org/WithSearchOrgContext.ts @@ -1,8 +1,12 @@ -import { ContextConsumer } from "@lit/context"; +import { ContextConsumer, type ContextCallback } from "@lit/context"; import type { LitElement } from "lit"; import type { Constructor } from "type-fest"; -import { searchOrgContext, searchOrgInitialValue } from "./search-org"; +import { + searchOrgContext, + searchOrgInitialValue, + type SearchOrgContext, +} from "./search-org"; import type { SearchOrgKey } from "./types"; /** @@ -17,8 +21,14 @@ export const WithSearchOrgContext = >( superClass: T, ) => class extends superClass { + protected searchOrgContextUpdated: ContextCallback = + () => {}; + readonly #searchOrg = new ContextConsumer(this, { context: searchOrgContext, + callback: (value) => { + this.searchOrgContextUpdated(value); + }, subscribe: true, }); diff --git a/frontend/src/features/collections/choose-collection-name.ts b/frontend/src/features/collections/choose-collection-name.ts new file mode 100644 index 0000000000..702ca4de64 --- /dev/null +++ b/frontend/src/features/collections/choose-collection-name.ts @@ -0,0 +1,23 @@ +import { localized } from "@lit/localize"; +// import { html } from "lit"; +import { customElement } from "lit/decorators.js"; + +import { SearchCombobox } from "@/components/ui/search-combobox"; +import type { SearchOrgContext } from "@/context/search-org"; +import { searchQueryKeys, type SearchQuery } from "@/context/search-org/types"; +import { WithSearchOrgContext } from "@/context/search-org/WithSearchOrgContext"; + +@customElement("btrix-choose-collection-name") +@localized() +export class ChooseCollectionName extends WithSearchOrgContext( + SearchCombobox, +) { + searchKeys = searchQueryKeys; + + searchOrgContextUpdated = (value: SearchOrgContext) => { + if (value.collections) { + // this.fuse = value.collections; + console.log("update fuse"); + } + }; +} From dd1fe5eb519281dbcb3f01bf157453ab3136cb22 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 7 Oct 2025 15:21:52 -0700 Subject: [PATCH 03/20] custom fuse --- frontend/src/components/ui/search-combobox.ts | 13 +++++++------ frontend/src/context/search-org/connectFuse.ts | 1 + .../features/collections/choose-collection-name.ts | 6 +++--- frontend/src/features/collections/index.ts | 1 + .../src/features/crawl-workflows/workflow-editor.ts | 4 ++-- frontend/src/utils/hasChanged.ts | 3 +++ 6 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 frontend/src/utils/hasChanged.ts diff --git a/frontend/src/components/ui/search-combobox.ts b/frontend/src/components/ui/search-combobox.ts index d0c80398d7..67b1b9d0c0 100644 --- a/frontend/src/components/ui/search-combobox.ts +++ b/frontend/src/components/ui/search-combobox.ts @@ -8,6 +8,7 @@ import { when } from "lit/directives/when.js"; import debounce from "lodash/fp/debounce"; import { type UnderlyingFunction } from "@/types/utils"; +import { hasChanged } from "@/utils/hasChanged"; type SelectEventDetail = { key: string | null; @@ -16,7 +17,7 @@ type SelectEventDetail = { export type SelectEvent = CustomEvent>; const MIN_SEARCH_LENGTH = 2; -const MAX_SEARCH_RESULTS = 10; +const MAX_SEARCH_RESULTS = 5; /** * Fuzzy search through list of options @@ -30,10 +31,10 @@ export class SearchCombobox extends LitElement { @property({ type: Array }) searchOptions: T[] = []; - @property({ type: Array }) + @property({ type: Array, hasChanged }) searchKeys: string[] = []; - @property({ type: Object }) + @property({ type: Object, hasChanged }) keyLabels?: { [key: string]: string }; @property({ type: String }) @@ -61,8 +62,8 @@ export class SearchCombobox extends LitElement { @query("sl-input") private readonly input!: SlInput; - private fuse = new Fuse([], { - keys: [], + protected fuse = new Fuse(this.searchOptions, { + keys: this.searchKeys, threshold: 0.2, // stricter; default is 0.6 includeMatches: true, }); @@ -79,7 +80,7 @@ export class SearchCombobox extends LitElement { } if (changedProperties.has("searchKeys")) { this.onSearchInput.cancel(); - this.fuse = new Fuse([], { + this.fuse = new Fuse(this.searchOptions, { ...( this.fuse as unknown as { options: ConstructorParameters[1]; diff --git a/frontend/src/context/search-org/connectFuse.ts b/frontend/src/context/search-org/connectFuse.ts index 5e080c0a09..7d262ead81 100644 --- a/frontend/src/context/search-org/connectFuse.ts +++ b/frontend/src/context/search-org/connectFuse.ts @@ -10,5 +10,6 @@ export function connectFuse(values: SearchQuery[]) { keys: searchQueryKeys, threshold: 0.3, useExtendedSearch: true, + includeMatches: true, }); } diff --git a/frontend/src/features/collections/choose-collection-name.ts b/frontend/src/features/collections/choose-collection-name.ts index 702ca4de64..74d70a171e 100644 --- a/frontend/src/features/collections/choose-collection-name.ts +++ b/frontend/src/features/collections/choose-collection-name.ts @@ -14,10 +14,10 @@ export class ChooseCollectionName extends WithSearchOrgContext( ) { searchKeys = searchQueryKeys; - searchOrgContextUpdated = (value: SearchOrgContext) => { + searchOrgContextUpdated = async (value: SearchOrgContext) => { if (value.collections) { - // this.fuse = value.collections; - console.log("update fuse"); + await this.updateComplete; + this.fuse = value.collections; } }; } diff --git a/frontend/src/features/collections/index.ts b/frontend/src/features/collections/index.ts index 7c43e87442..8006df36b6 100644 --- a/frontend/src/features/collections/index.ts +++ b/frontend/src/features/collections/index.ts @@ -1,3 +1,4 @@ +import("./choose-collection-name"); import("./collections-add"); import("./collections-grid"); import("./collections-grid-with-edit-dialog"); diff --git a/frontend/src/features/crawl-workflows/workflow-editor.ts b/frontend/src/features/crawl-workflows/workflow-editor.ts index af033be6da..253a7fef73 100644 --- a/frontend/src/features/crawl-workflows/workflow-editor.ts +++ b/frontend/src/features/crawl-workflows/workflow-editor.ts @@ -2325,12 +2325,12 @@ https://archiveweb.page/images/${"logo.svg"}`} return html` ${this.renderSectionHeading(msg("Set Collection"))} ${inputCol(html` - - + `)} ${this.renderHelpTextCol( msg( diff --git a/frontend/src/utils/hasChanged.ts b/frontend/src/utils/hasChanged.ts new file mode 100644 index 0000000000..b08a46facb --- /dev/null +++ b/frontend/src/utils/hasChanged.ts @@ -0,0 +1,3 @@ +import isEqual from "lodash/fp/isEqual"; + +export const hasChanged = (a: unknown, b: unknown) => !isEqual(a, b); From c68e404caee2561f40d1116585935237e0eee7cc Mon Sep 17 00:00:00 2001 From: sua yoo Date: Wed, 8 Oct 2025 11:55:16 -0700 Subject: [PATCH 04/20] update size --- frontend/src/components/ui/search-combobox.ts | 4 ++-- frontend/src/features/archived-items/item-list-controls.ts | 1 + frontend/src/pages/org/archived-items.ts | 1 + frontend/src/pages/org/workflows-list.ts | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/ui/search-combobox.ts b/frontend/src/components/ui/search-combobox.ts index 67b1b9d0c0..860b972e5b 100644 --- a/frontend/src/components/ui/search-combobox.ts +++ b/frontend/src/components/ui/search-combobox.ts @@ -50,7 +50,7 @@ export class SearchCombobox extends LitElement { label?: string; @property({ type: String }) - size: SlInput["size"] = "small"; + size?: SlInput["size"]; private get hasSearchStr() { return this.searchByValue.length >= MIN_SEARCH_LENGTH; @@ -123,9 +123,9 @@ export class SearchCombobox extends LitElement { }} > { diff --git a/frontend/src/features/archived-items/item-list-controls.ts b/frontend/src/features/archived-items/item-list-controls.ts index ed2b20b892..a331b08666 100644 --- a/frontend/src/features/archived-items/item-list-controls.ts +++ b/frontend/src/features/archived-items/item-list-controls.ts @@ -96,6 +96,7 @@ export class ItemListControls extends TailwindElement { .searchOptions=${this.searchOptions} selectedKey=${ifDefined(this.selectedSearchFilterKey)} placeholder=${msg("Filter by name")} + size="small" @btrix-select=${(e: SelectEvent) => { const { key, value } = e.detail; if (key) { diff --git a/frontend/src/pages/org/archived-items.ts b/frontend/src/pages/org/archived-items.ts index 53b35e5635..94ae6b10de 100644 --- a/frontend/src/pages/org/archived-items.ts +++ b/frontend/src/pages/org/archived-items.ts @@ -720,6 +720,7 @@ export class CrawlsList extends BtrixElement { .searchKeys=${this.searchKeys} .searchOptions=${this.searchOptions} .keyLabels=${CrawlsList.FieldLabels} + size="small" selectedKey=${ifDefined(this.selectedSearchFilterKey)} searchByValue=${ifDefined( this.selectedSearchFilterKey && diff --git a/frontend/src/pages/org/workflows-list.ts b/frontend/src/pages/org/workflows-list.ts index b629fcc281..813de62c3f 100644 --- a/frontend/src/pages/org/workflows-list.ts +++ b/frontend/src/pages/org/workflows-list.ts @@ -744,6 +744,7 @@ export class WorkflowsList extends BtrixElement { .searchKeys=${this.searchKeys} .searchOptions=${this.searchOptions} .keyLabels=${WorkflowsList.FieldLabels} + size="small" selectedKey=${ifDefined(this.selectedSearchFilterKey)} placeholder=${msg("Search all workflows by name or crawl start URL")} @btrix-select=${(e: SelectEvent) => { From 126b5df395326542fbcffdc1108e4c2307f84d35 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Wed, 8 Oct 2025 14:40:16 -0700 Subject: [PATCH 05/20] use standard select event --- frontend/src/components/ui/search-combobox.ts | 22 ++++++++++--------- .../archived-items/item-list-controls.ts | 6 ++--- frontend/src/pages/org/archived-items.ts | 8 +++++-- frontend/src/pages/org/workflows-list.ts | 6 ++--- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/ui/search-combobox.ts b/frontend/src/components/ui/search-combobox.ts index 860b972e5b..d049821fba 100644 --- a/frontend/src/components/ui/search-combobox.ts +++ b/frontend/src/components/ui/search-combobox.ts @@ -7,14 +7,14 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { when } from "lit/directives/when.js"; import debounce from "lodash/fp/debounce"; +import type { BtrixSelectEvent } from "@/events/btrix-select"; import { type UnderlyingFunction } from "@/types/utils"; import { hasChanged } from "@/utils/hasChanged"; -type SelectEventDetail = { +export type BtrixSearchComboboxSelectEvent = BtrixSelectEvent<{ key: string | null; - value?: T; -}; -export type SelectEvent = CustomEvent>; + value: string; +}>; const MIN_SEARCH_LENGTH = 2; const MAX_SEARCH_RESULTS = 5; @@ -110,15 +110,17 @@ export class SearchCombobox extends LitElement { this.searchResultsOpen = false; const item = e.detail.item as SlMenuItem; const key = item.dataset["key"]; - this.searchByValue = item.value; + this.searchByValue = item.value || ""; await this.updateComplete; this.dispatchEvent( - new CustomEvent>("btrix-select", { - detail: { - key: key ?? null, - value: item.value as T, + new CustomEvent( + "btrix-select", + { + detail: { + item: { key: key ?? null, value: this.searchByValue }, + }, }, - }), + ), ); }} > diff --git a/frontend/src/features/archived-items/item-list-controls.ts b/frontend/src/features/archived-items/item-list-controls.ts index a331b08666..82ca0005b5 100644 --- a/frontend/src/features/archived-items/item-list-controls.ts +++ b/frontend/src/features/archived-items/item-list-controls.ts @@ -6,7 +6,7 @@ import { customElement, property, state } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; import { TailwindElement } from "@/classes/TailwindElement"; -import { type SelectEvent } from "@/components/ui/search-combobox"; +import { type BtrixSearchComboboxSelectEvent } from "@/components/ui/search-combobox"; export type FilterBy = Partial>; export type SearchValues = { @@ -97,8 +97,8 @@ export class ItemListControls extends TailwindElement { selectedKey=${ifDefined(this.selectedSearchFilterKey)} placeholder=${msg("Filter by name")} size="small" - @btrix-select=${(e: SelectEvent) => { - const { key, value } = e.detail; + @btrix-select=${(e: BtrixSearchComboboxSelectEvent) => { + const { key, value } = e.detail.item; if (key) { this.dispatchEvent( new CustomEvent("btrix-filter-change", { diff --git a/frontend/src/pages/org/archived-items.ts b/frontend/src/pages/org/archived-items.ts index 94ae6b10de..06a5f5b0e2 100644 --- a/frontend/src/pages/org/archived-items.ts +++ b/frontend/src/pages/org/archived-items.ts @@ -16,6 +16,7 @@ import { type FilterChip, } from "@/components/ui/filter-chip"; import { parsePage, type PageChangeEvent } from "@/components/ui/pagination"; +import type { BtrixSearchComboboxSelectEvent } from "@/components/ui/search-combobox"; import { ClipboardController } from "@/controllers/clipboard"; import { SearchParamsValue } from "@/controllers/searchParamsValue"; import { type BtrixChangeArchivedItemStateFilterEvent } from "@/features/archived-items/archived-item-state-filter"; @@ -731,8 +732,11 @@ export class CrawlsList extends BtrixElement { : this.itemType === "crawl" ? msg("Search all crawls by name or crawl start URL") : msg("Search all items by name or crawl start URL")} - @btrix-select=${(e: CustomEvent) => { - const { key, value } = e.detail; + @btrix-select=${(e: BtrixSearchComboboxSelectEvent) => { + const { key, value } = e.detail.item; + + if (key == null) return; + this.filterBy.setValue({ ...this.filterBy.value, [key]: value, diff --git a/frontend/src/pages/org/workflows-list.ts b/frontend/src/pages/org/workflows-list.ts index 813de62c3f..661e3389e0 100644 --- a/frontend/src/pages/org/workflows-list.ts +++ b/frontend/src/pages/org/workflows-list.ts @@ -20,7 +20,7 @@ import type { FilterChip, } from "@/components/ui/filter-chip"; import { parsePage, type PageChangeEvent } from "@/components/ui/pagination"; -import { type SelectEvent } from "@/components/ui/search-combobox"; +import type { BtrixSearchComboboxSelectEvent } from "@/components/ui/search-combobox"; import { SearchParamsController } from "@/controllers/searchParams"; import type { SelectJobTypeEvent } from "@/features/crawl-workflows/new-workflow-dialog"; import { @@ -747,8 +747,8 @@ export class WorkflowsList extends BtrixElement { size="small" selectedKey=${ifDefined(this.selectedSearchFilterKey)} placeholder=${msg("Search all workflows by name or crawl start URL")} - @btrix-select=${(e: SelectEvent) => { - const { key, value } = e.detail; + @btrix-select=${(e: BtrixSearchComboboxSelectEvent) => { + const { key, value } = e.detail.item; if (key == null) return; this.filterBy = { [key]: value, From 1c8715cca5fff587b980ca81ac4f4dcc7c3869ab Mon Sep 17 00:00:00 2001 From: sua yoo Date: Thu, 9 Oct 2025 14:02:26 -0700 Subject: [PATCH 06/20] add wip form control --- .vscode/settings.json | 12 +- frontend/src/components/ui/combobox.ts | 4 +- frontend/src/components/ui/search-combobox.ts | 121 +++++++++++++----- .../src/context/search-org/connectFuse.ts | 12 +- frontend/src/events/btrix-select.ts | 2 +- .../collections/choose-collection-name.ts | 114 ++++++++++++++++- .../crawl-workflows/workflow-editor.ts | 30 +++-- frontend/src/pages/org/workflows-new.ts | 1 + frontend/src/types/crawler.ts | 2 +- frontend/src/utils/workflow.ts | 3 + 10 files changed, 243 insertions(+), 58 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d5a5c334d5..738c15cfc4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,7 @@ "Browsertrix", "btrix", "clsx", + "dedup", "Elems", "favicons", "hoverable", @@ -47,11 +48,16 @@ ] } ], - "eslint.workingDirectories": ["./frontend"], + "eslint.workingDirectories": [ + "./frontend" + ], "eslint.nodePath": "./frontend/node_modules", - "tailwindCSS.experimental.classRegex": ["tw`([^`]*)", "clsx\\(([^)]*)\\)"], + "tailwindCSS.experimental.classRegex": [ + "tw`([^`]*)", + "clsx\\(([^)]*)\\)" + ], "html.customData": [ "./node_modules/@shoelace-style/shoelace/dist/vscode.html-custom-data.json" ], "prettier.configPath": "./frontend/prettier.config.js" -} +} \ No newline at end of file diff --git a/frontend/src/components/ui/combobox.ts b/frontend/src/components/ui/combobox.ts index 46ebf29701..438d08c99e 100644 --- a/frontend/src/components/ui/combobox.ts +++ b/frontend/src/components/ui/combobox.ts @@ -79,9 +79,7 @@ export class Combobox extends LitElement { @keyup=${this.onKeyup} @focusout=${this.onFocusout} > -
- -
+