The 8 flow-view search inputs used lodash/debounce via useMemo(()=>debounce(fn,500))
+ .cancel() cleanup. Swap to useDebouncedCallback(fn,500) from use-debounce — already
the repo idiom (data-table, input-search, use-table-state) with identical
leading:false/trailing:true semantics and the same .cancel() API. Drops lodash +
@types/lodash (a whole general-purpose lib kept for one function).
tsc + lint + build + full vitest (813) green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 7 base UI components still on @radix-ui/react-icons now use lucide-react
(already the dominant icon lib, ~79 files). 1:1 name map: Cross2->X,
DragHandleDots2->GripVertical, Chevron{Down,Right,Up}, DotsHorizontal->MoreHorizontal,
MagnifyingGlass->Search, Check, DotFilled->Circle (kept a small filled dot for the
radio indicator via h-2 w-2 fill-current). One icon library instead of two.
tsc + lint + build + full vitest (813) green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- @tiptap/extension-{code,code-block,hard-break}: bundled transitively by
@tiptap/starter-kit, never imported directly in src — remove the redundant
top-level pins (functionality unchanged, editor tests 161/161 green).
- vaul + src/components/ui/drawer.tsx: dead shadcn wrapper with zero consumers.
tsc + lint + build + editor suite all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the unified rich editor's markdown engine: drop the community
tiptap-markdown (markdown-it parse + prosemirror-markdown serialize) and
our 5 prosemirror-internal monkey-patch extensions for the official
@tiptap/markdown (marked-based MarkdownManager). The consumer moves to the
new API (editor.getMarkdown() / contentType:'markdown' / markdown.parse).
editor-markdown.ts adds three small, supported-API customizations:
- a private HTML-neutralized marked instance so literal <xml-tags> survive
(marked otherwise swallows real-HTML-element names like <input>);
- FaithfulMarkdownText overrides MarkdownManager.encodeTextForMarkdown to
drop entity-encoding and over-escaping of literal punctuation;
- MarkdownTable wraps renderTableToMarkdown to escape cell pipes (#7884).
Verified: tsc, 747 vitest (rewritten extension + 39-prompt corpus tests),
lint, build; live on the dev stand — knowledge tables render and resize,
prompt <tags> stay literal (124 tag + 116 variable highlights, no
entity-encoding), console clean.
Known accepted bug, pinned by a test (marked parser, 0 corpus impact):
a code block nested ordered-list > bullet-sublist > code is dropped on
parse. Repro recorded for an upstream report.
Folds in the in-progress unified-editor work it depends on: knowledge
Plain/Visual toggle, settings-prompt and template editor wiring, the
CodeMirror removal, and the editor table/tag/variable styles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prompt templates are Go templates with markdown tables, <xml-tags>, and
significant whitespace — content a markdown (Tiptap) editor normalizes and
corrupts on round-trip. Add a CodeMirror-based view instead, toggled from the
prompt page actions menu, that edits the raw template verbatim.
- New shared CodeEditor (CodeMirror 6 + markdown highlighting, line wrapping,
theme-aware), lazy-loaded so its chunk only loads when code view is opened.
- "Code editor" / "Plain text" toggle in the prompt actions menu.
- Available-variables click inserts {{.Var}} into the code editor at the caret.
- Fix a pre-existing tsc error in the variable-cycling code
(noUncheckedIndexedAccess on the matched-occurrence lookup).
Round-trip verified byte-exact end to end: original -> code edit -> plain, and
save -> DB -> reload, all identical to source (tables, tags, whitespace intact).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commitlint had no config anywhere (no config file, no husky commit-msg
hook), so its rules were never loaded; the "commit" script also pointed
at an uninstalled binary. Remove @commitlint/cli and
@commitlint/config-conventional, the dead "commit"/"commitlint" scripts,
and the stale commitlint entry in README Development Requirements.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Confirmed unused via depcheck + manual verification:
- anser, js-cookie, @types/js-cookie — no imports anywhere
- rehype-raw — only referenced in a vite manualChunks regex, never
imported as a markdown plugin (markdown.tsx uses rehype-highlight/slug
and remark-gfm); also dropped from that regex
- @graphql-codegen/{client-preset,near-operation-file-preset,typescript}
— codegen config uses explicit plugins (typescript-operations,
typed-document-node), not these presets/base plugin
Also drop the dead package.json "eslintConfig" block — it is ignored by
the flat config (eslint.config.mjs) and referenced an uninstalled
storybook plugin.
Verified: graphql:generate reproduces src/graphql/types.ts unchanged,
plus build, lint, 604 tests, and --frozen-lockfile all pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The plugin was a direct devDependency but never enabled in
eslint.config.mjs (0 active jsx-a11y rules via --print-config). Dropping
it removes dead weight and one stale eslint-9 peer constraint. Lint still
passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add frontend/.nvmrc (24.17.0) as the single source of truth for Node;
GitHub CI reads it via node-version-file, Dockerfile uses node:24.17.0-slim
- bump packageManager to pnpm@11.8.0; drop "corepack prepare pnpm@latest"
so the pnpm version derives from packageManager everywhere
- migrate pnpm onlyBuiltDependencies -> allowBuilds in pnpm-workspace.yaml
(the package.json "pnpm" field is no longer read by pnpm 11)
- add a CI step that fails if the Dockerfile Node tag drifts from .nvmrc
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
vite build strips types and never type-checks, and the old build`s bare tsc (solution config, files:[]) checked nothing — so type errors reached main unnoticed (which is how 76 had accumulated). Now they fail fast everywhere.
- build: `tsc -b && vite build` — `pnpm build` (and the Docker image build, which runs `pnpm run build`) fails on any type error before bundling.
- add `typescript` script (`tsc -b`, checks both app + node project configs).
- ci.yml: blocking "Frontend - Type check" step in lint-and-test (runs on every branch push; docker-build only runs on main/tags).
Verified: a type error makes both `pnpm run typescript` and `pnpm build` exit non-zero (vite never runs); removing it restores a clean build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the deprecated typescript-react-apollo codegen plugin (unmaintained for Apollo Client v4) with typed-document-node. All ~105 useXxxQuery/Mutation/Subscription call sites are rewritten to the generic useQuery/useMutation/useSubscription(XxxDocument, ...) form, with skipToken replacing skip + conditional-variables.
graphql-codegen.ts: plugins typescript-operations + typed-document-node; add scalars { Time: string } (was unknown); drop withHooks/apolloReact* options. tsconfig.app/node.json: ignoreDeprecations "6.0" for the baseUrl TS5101 deprecation. Regenerate src/graphql/types.ts.
Behavior-preserving: variables, fetchPolicy, error handling, and subscription onData/refetchQueries are unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
monaco-terminal.tsx had no importers; drop it together with the now-unused
monaco-editor and @monaco-editor/react dependencies (~3 MB). The component is
preserved on branch feature/frontend-monaco-terminal for later work.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an opt-in `isVirtualized` prop on DataTable, wired through
@tanstack/react-virtual's useWindowVirtualizer. Threshold gates short
tables (preserves Find-in-page, screen-reader enumeration). Padding <tr>
sentinels keep native HTML table semantics. Skipped when
`renderSubComponent` is set — expanded rows would need per-row
remeasurement we don't wire here.
Enabled on /flows. Measured with pageSize=All (1116 rows):
DOM elements: 41,657 → 1,121 (37x less)
tbody <tr>: 1,116 → 22
a11y tree size: ~485 KB → ~80 KB
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After landing the imperative-timer fix and extracting `useDebouncedCallback`
into our own hook, the use-site count climbed past three and the case for
a single, battle-tested contract beat the case for in-tree code. Drops
both our `useDebouncedValue` and `useDebouncedCallback` and routes every
caller through `use-debounce@10.1.1` (1M+ weekly, ~3KB gzip).
API mapping at each use-site:
- value debounce: `useDebouncedValue(v, ms)` → `const [v] = useDebounce(v, ms)`
- callback debounce: `useDebouncedCallback(fn, ms)` → identical signature,
same `.cancel()`/`.isPending()` semantics we relied on, plus `.flush()`
and leading/trailing/maxWait options we now get for free.
Files touched (7 use-sites): data-table.tsx, input-search.tsx,
use-table-state.ts, use-table-query-filter.ts, knowledges-provider.tsx,
use-flow-files-search.ts, use-detail-navigation.ts.
pnpm + Vite gotcha: pnpm hoists into `.pnpm/<name>@<ver>/`, so any
transitive `import React from 'react'` from a third-party package can
resolve to its own physical copy. With one path through our code and
another through use-debounce, the runtime crashed at `useRef` with
"Cannot read properties of null (reading 'useRef')". Added
`resolve.dedupe: ['react', 'react-dom']` in vite.config.ts so the
bundle ships exactly one React.
Tests:
- new input-search.test.tsx (10 tests) covers collapsed/expanded
presentation, expand-on-click + rAF focus, burst-typing coalesce,
Esc-clear mid-debounce, two-stage Esc semantics, external-source-wins
race, Ctrl+F / Cmd+F global hotkey expansion.
- data-table.test.tsx +5 tests: deep-link initial filterValue,
6-keystroke burst coalesce, unmount-mid-debounce cleanup, two
independent DataTables on one page (settings/prompts shape),
back-button-style external clear, three-round type/clear stress.
- full suite: 541/541 passing, lint 0 errors.
Manual smoke via Chrome DevTools MCP on every page that mounts a
filter input — /flows, /templates, /settings/api-tokens,
/settings/prompts (both tables independent), /knowledges (filter
+ semantic search hitting the server through ?qs=). All scenarios
collapse to a single `[{v="", u=""}]` transition; no URL re-emit
after X, no leftover timer after Esc, no cross-table leakage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New `components/ui/input-search.tsx`: a controlled, collapsible search
control that idles as just a `<Search>` icon button and expands into a
text input with a motion-driven width animation. Adapted from the
sofy-front version with:
- Trigger is a real `<button>` (via `InputGroupButton`) instead of a
`<div onClick>` — keyboard reachable, screen-reader announced, and
avoids the invalid-HTML nesting that putting an `<input>` inside a
`<button>` would create.
- Configurable Mod+`hotkey` (default `'f'`, `null` to disable). The
knowledges page uses `'k'` so it doesn't fight the browser's own
Ctrl+F find-in-page.
- Escape clears the value on the first press, blurs + collapses on the
second — matches native search-input muscle memory.
- Single collapse effect on `searchQuery` change instead of the
duplicate blur+effect pair.
- `aria-label` prop wired to both the trigger button and the input.
The knowledges header now mounts this control bound to `?qs=` via
`useSearchParams`. The provider already debounces the URL value before
hitting `searchKnowledge`, so the input commits keystrokes immediately
(instant UI feedback) while the server roundtrip waits for the 400 ms
debounce. URL writes use `replace` to avoid history-stack churn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Runs pnpm update + bumps prettier-plugin-tailwindcss 0.7.4 → 0.8.0
(the 0.8 release is the one that pairs with Tailwind v4).
After this commit, pnpm outdated only reports three deliberately
pinned packages: eslint / @eslint/js (held on 9.x while
eslint-plugin-react still targets eslint ^9) and @types/node (held on
24.x to match the Node 24.12 runtime; npm latest is 25.x for Node 25).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps @types/node 22.19.19 → 24.12.4 to match the actual Node 24.12
runtime the project is built and run on. The latest published tag is
25.x (for the not-yet-released Node 25 line); we deliberately stay on
24.x.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps @commitlint/cli and @commitlint/config-conventional from 20.5.3
to 21.0.1. Dev-only — affects the commit-message hook, not the app.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps typescript 5.9.3 → 6.0.3.
Adds compilerOptions.ignoreDeprecations = "6.0" to tsconfig.json so the
existing baseUrl/paths setup keeps working — TS 6 has flagged baseUrl
as deprecated (it will be removed in 7.0). The vite-tsconfig-paths
plugin still consumes the paths mapping; nothing else in the codebase
needed changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps eslint-plugin-perfectionist 4.15.1 → 5.9.0.
eslint and @eslint/js are intentionally kept on 9.39.4 (the latest
9.x): eslint 10 changes the rule context API
(contextOrFilename.getFilename is gone) and eslint-plugin-react still
ships only versions targeting eslint ^9 — the v10 upgrade currently
crashes the lint run with "Error while loading rule
'react/display-name'". Once eslint-plugin-react publishes a v10-aware
release we can finish this bump.
perfectionist v5 already supports the eslint ^8.45 || ^9 || ^10 peer
range, so it ships cleanly today and gives us its v5 rule changes
without dragging eslint along.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps vite 7.3.3 → 8.0.13 and vite-tsconfig-paths 5.1.4 → 6.1.1.
Vite 8 ships rolldown (the Rust bundler that replaced rollup) and that
required two fixes:
1. build.rollupOptions.output.manualChunks: rolldown no longer accepts
the { chunkName: [packages] } static object form, so it is now an
(id) => string callback that matches paths under node_modules.
2. src/components/ui/resizable.tsx: switched from the namespace import
`import * as ResizablePrimitive` to named `{ Group, Panel, Separator }`
from react-resizable-panels. The v4 release of that library renamed
PanelGroup → Group and PanelResizeHandle → Separator; rollup's
loose tree-shaking happened to keep the old names working at
build-time, but rolldown enforces the export list and the page
failed at runtime with "Element type is invalid". Public wrapper
exports (ResizableHandle, ResizablePanel, ResizablePanelGroup) are
unchanged so the rest of the app keeps working.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps the codegen stack: cli 6→7, client-preset 5→6, typescript 5→6,
typescript-operations 5→6 (typescript-react-apollo and
near-operation-file-preset only moved within their minor range).
Configures typescript-react-apollo to emit Apollo v4-compatible imports
via apolloReactCommonImportFrom / apolloReactHooksImportFrom set to
@apollo/client/react, so re-running pnpm run graphql:generate now
produces ApolloReactCommon / ApolloReactHooks namespaces from the v4
subpath instead of the v3 single-entry import.
src/graphql/types.ts is fully regenerated under the new config.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps @apollo/client from 3.14.1 to 4.1.9.
Apollo v4 moved React-only exports to the @apollo/client/react
subpath. Updated the three import sites that consume them:
- ApolloProvider in app.tsx
- useApolloClient in providers/resources-provider.tsx
- the Apollo namespace import in graphql/types.ts (covers useQuery,
useMutation, useSubscription, useLazyQuery, useSuspenseQuery,
skipToken, *HookOptions, *QueryResult)
Everything else (gql, ApolloClient, ApolloLink, createHttpLink,
InMemoryCache, Observable, split, NetworkStatus, getMainDefinition,
onError, GraphQLWsLink) keeps its existing import path in v4.
Build output shrunk from 223 kB to 191 kB for the apollo-client chunk.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps zod 3.25.76 → 4.4.3 and @hookform/resolvers 3.10.0 → 5.2.2
(the resolvers v5 requires zod v4).
zod is used in 25 source files (schemas, form validators, table state
parsers). Build passes without changes — the project relies on the
shared subset of the v3/v4 API (z.object/z.string/z.number/z.enum,
z.infer, .min/.max/.optional, refinements). Runtime form validation
keeps disabling submit until required fields are filled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps react-resizable-panels from 3.0.6 to 4.11.1. The wrapper in
src/components/ui/resizable.tsx only re-exports PanelGroup, Panel and
PanelResizeHandle; their props remain compatible across the major.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps react-day-picker from 9.14.0 to 10.0.1. The Calendar wrapper in
src/components/ui/calendar.tsx uses the v9 classNames keys
(button_next/previous, day_button, range_*, week, weekday, weekdays)
and the components.Chevron API; all of these are preserved in v10.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps marked from 17.0.6 to 18.0.3. Used only in
src/lib/report/report-pdf.tsx for PDF generation via marked.lexer();
that API is compatible across the major.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps lucide-react from 0.553.0 to 1.16.0. The 0.x → 1.x jump is a
versioning policy change, not a breaking API change — all icon imports
keep working as before.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pin React/React-DOM to 19.2.6 and @types/react* to 19.2.0 in
package.json — lockfile already resolved the latest 19.x; this just
makes the minimum explicit so future installs don't regress.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- @react-pdf/renderer (~1.5 MB) moved to an async chunk. lib/report.ts
used to statically re-export PDF helpers from ./report-pdf, which
pulled the entire PDF library into every page that imports
lib/report (flow.tsx, flow-report.tsx). The re-exports are now
async wrappers using `await import('./report-pdf')`, so the
library loads only when the user actually triggers a PDF export.
Initial JS for /flows/:id/report drops from ~2.0 MB to ~500 KB.
- Drop html2pdf.js: zero imports anywhere in the source, but it was
declared as a dependency and reserved a (now-empty) manual chunk in
vite.config.ts. Removing it also drops 22 transitive packages from
the lockfile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- src/lib/сlipboard.ts → clipboard.ts: the filename used a Cyrillic 'с'
(U+0441) instead of Latin 'c', which made the path silently
unmatchable by anyone typing the import with a Latin letter. Update
the 4 importers in features/flows.
- Delete unreachable settings-mcp-server(s).tsx (~1.3k lines): no
routes registered in app.tsx and nothing imports them. Remove the
matching dead branches and commented menu entry from
settings-layout.tsx.
- Drop unused dev deps simple-git-hooks and lint-staged: no config in
the repo, not used by CI or Docker, hooks were never wired up. Also
remove simple-git-hooks from pnpm.onlyBuiltDependencies.
- Drop unused tsconfig path aliases: @/ui/*, @env (env.ts doesn't even
exist), and @pkg. Only @/* remains.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- replace plain textarea with a tiptap-based markdown editor (StarterKit, tiptap-markdown, placeholder) and a basic formatting toolbar so authors can edit knowledge content with rich formatting while keeping markdown as the storage format for embeddings
- introduce a reusable MarkdownEditor shared component and a tiptap Storage type augmentation
- move the save action from the input addon into the page header on the right
- give each desktop resizable panel its own internal scroll so long meta or content no longer makes the whole page scroll
Co-authored-by: Cursor <cursoragent@cursor.com>
- introduce src/components/file-manager with tree rendering, search highlighting, multi-select (single / toggle / shift-range), keyboard navigation (arrows, Home/End, Space, Cmd+A, Esc) and bulk delete
- expose unified actions[] API with downloadAction / copyPathAction / deleteAction factory helpers instead of separate built-in props
- split orchestration into useFileManagerExpansion and useFileManagerSelection hooks; derive expanded/selected state during render without useEffect, preserving Set identity for memo-friendly downstream
- add ARIA tree semantics (role=tree on the container, treeitem rows, roving tabindex via activeRowPath, aria-expanded/aria-selected)
- pure tree utils: O(1) folder lookup via Map, normalizeRootGroups, dedupeOverlappingPaths, findNodeByPath, plus 37 vitest unit tests
- ship shadcn checkbox primitive (depends on @radix-ui/react-checkbox) which the file manager uses for row and select-all controls
Made-with: Cursor