The mobile period switcher swap landed in 28c98a8 but kept the
text-button paddings (px-3), so each "icon-only" trigger was still
~36 px wide and the group looked stretched on a 375 px viewport.
Added \`aspect-square px-0\` to each TabsTrigger (with the desktop
overrides reset above the \`sm\` breakpoint) so the buttons collapse
to a 1:1 ratio on mobile — three tight squares matching shadcn's
icon-button proportions — while desktop keeps the original
horizontal padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On screens narrower than the `sm` breakpoint (640 px) the
Week / Month / Quarter triggers were forcing the period switcher to
crowd the Analytics / Overview tabs into the next line. Replaced
the labels with lucide calendar icons (CalendarDays / Calendar /
CalendarRange) that swap in below `sm` while the full text comes
back above it.
Accessibility preserved — each TabsTrigger keeps an explicit
aria-label and the icons are aria-hidden so screen readers still
announce "Week" / "Month" / "Quarter" instead of duplicate icon
descriptions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both tab groups on /dashboard trigger expensive re-renders:
- Analytics ↔ Overview swaps the entire content subtree (Analytics
alone pulls in a ~386 kB chunk with four Recharts views).
- Week / Month / Quarter invalidates the analytics query and
re-paints every chart with the new dataset.
Wrapped both setState calls in startTransition so the trigger
buttons update on the urgent track while the heavy work commits as
low-priority. React will discard intermediate frames if the user
clicks again before the previous transition finishes.
Surfaces the combined isPending state via a subtle opacity-60 dim
plus aria-busy on the content region — gives users feedback that
the heavier render is in flight without a spinner flash.
Verified: build, lint (0/44 baseline), 475/475 tests, browser smoke
on /dashboard with no console errors after switching periods.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings every top-level React component and hook outside of
src/components/{ui,shared} (those were already migrated in 9d5daf7)
in line with the shadcn/ui new-york-v4 canonical style:
- features/* (flows, knowledges, resources, authentication, …)
- pages/* (dashboard, flows, knowledges, settings, templates, login,
oauth-result, resources)
- providers/* (all eleven context providers + their useXxx hooks)
- components/icons/* (every SVG icon + provider-icon, flow-status-*)
- components/layouts/* and components/routes/*
- hooks/* (use-* hooks in their own files)
- Calendar UI component (missed in the earlier pass — also dropped
the redundant `displayName = 'Calendar'` and the empty
`CalendarProps = DayPickerProps` alias)
Net diff is +50 lines (1842 ins / 1792 del — the lines that were
removed are the `const X = (…) =>` declarations and the matching
closing `;`, plus a handful of explicit `React.FC<…>` type
annotations that no longer add anything; the lines added are the
`function X(…) {`/`}` envelopes).
What we deliberately did NOT touch:
- Inner components/hooks defined inside other functions — keep their
arrow form so the local-scope intent stays visible.
- React.memo / React.lazy wrappers — `const X = memo(InnerX)` and
`const X = lazy(() => …)` aren't components themselves.
- Render-callback props (renderFlowItem, renderItem, …).
- Plain utilities, helpers, factories, REST/GraphQL builders and
every file under lib/, models/, schemas/, types/, graphql/types.ts.
- The few callback-ref-composing components (autocomplete, textarea,
markdown-editor) keep their explicit `ref?: Ref<CustomShape>`
because ComponentProps can't express their custom imperative shape.
Bonus cleanup along the way: removed the now-unused
`import * as React` from settings-prompt.tsx (was only there for the
deleted `React.FC` typing).
Verified: pnpm run build, lint (0 errors / 44 warnings unchanged
baseline) and 475/475 vitest tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes two leftover `X.displayName = 'X'` lines that just duplicated
the function's own name:
- src/components/shared/terminal/terminal.tsx
- src/components/shared/monaco-terminal.tsx
Two other displayName usages in the wider codebase are kept on
purpose:
- markdown.tsx — factory creates an inner `Renderer` and stamps it
`Highlighted(${ComponentName})` so each instance shows up
distinctly in React DevTools.
- file-manager-row.tsx — the function is named `FileManagerRowImpl`
and re-exported through React.memo as `FileManagerRow`; the
displayName carries the public name across the memo wrapper.
The five `.displayName` mentions in src/pages/settings/settings-prompt*
are property reads (`promptInfo.displayName`, `row.original.displayName`)
on domain data, not component metadata — untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings all 32 components in src/components/{ui,shared} in line with
the current shadcn/ui new-york-v4 canon (cross-checked against the
upstream registry tooltip / button / input / dialog sources). Net
diff is -637 lines of boilerplate.
Four shape changes per component:
1. function declaration instead of `const X = (...) =>`. Slightly
cleaner stack traces and Function.name is set automatically.
2. React.ComponentProps<typeof X> (or React.ComponentProps<"button">
for native elements) instead of
React.ComponentPropsWithoutRef<typeof X> & { ref?: React.Ref<...> }.
In React 19 ref is already part of ComponentProps; the explicit
intersection was leftover noise.
3. `ref` is no longer destructured — it flows through `{...props}`
spread into the underlying primitive. Exceptions kept verbatim:
textarea.tsx (custom TextareaRef shape with useImperativeHandle),
autocomplete.tsx (callback-ref composition in AutocompleteInput
and AutocompleteContent), markdown-editor.tsx (MarkdownEditorHandle
with useImperativeHandle).
4. displayName removed everywhere — Function.name covers React DevTools
now that there is no forwardRef wrapper to hide it.
Verified: pnpm run build, lint (0 errors / 44 warnings unchanged
baseline) and 475/475 vitest tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a tiny <PageTitle> wrapper around React 19's native <title>
element (which React hoists into <head> automatically) and uses it
from every page-level route. Browser tabs, history, and shareable
URLs now reflect the actual page instead of the static "PentAGI"
that index.html ships.
- Static titles for list and form pages (Dashboard, Flows, New flow,
Flow report, Templates, Knowledges, Resources, Providers, Prompts,
API tokens, Login, OAuth).
- Dynamic titles for detail pages — flow title + id, knowledge
question, template name, provider name, prompt display name — with
graceful fallbacks while data is still loading.
- Added to every render branch (loading / error / empty / main) so
the tab title stays correct even before data lands.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migrates all 32 components in src/components/{ui,shared} from
React.forwardRef<RefT, PropsT> to plain functional components that
take `ref?: React.Ref<RefT>` as a regular prop. React 19 treats ref
like any other prop, so the forwardRef wrapper is no longer needed
and only added boilerplate + an extra type parameter.
Highlights:
- All 32 files keep their displayName so React DevTools and Radix
Slot composition still surface readable component names.
- Custom-shape refs (TextareaRef in textarea.tsx and
MarkdownEditorHandle in markdown-editor.tsx) plus the callback-ref
composition inside autocomplete.tsx are preserved verbatim — only
the wrapper changes.
- sidebar.tsx contained 22 forwardRef wrappers in one file; all
converted in the same pass.
- No behaviour, classes, attributes or imports of `import * as React`
are touched. pnpm run lint:fix afterwards reformatted the auto-added
blank lines so we are back to 0 errors / 44 warnings (baseline).
Verified: pnpm run build, lint, and 475/475 vitest tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps the composite globalFilter ({ columns, query }) in
React.useDeferredValue before handing it to TanStack's state. The
filter input itself stays urgent (already debounced to 300 ms), so
keystrokes still feel instant; what becomes low-priority is the
heavy getFilteredRowModel() recomputation that runs over the full
dataset — ≈353 flows, 180 knowledge documents, ~50 templates and
API tokens. On those screens React can now drop intermediate filter
states when the user is still typing, instead of committing each
debounce tick on the urgent track.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- detail-navigation-buttons.tsx: wrap long cn() call across lines
- knowledge-header.tsx: alphabetize lucide-react named imports
- flow.tsx: reorder Tailwind classes per prettier-plugin-tailwindcss
canonical order
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops pnpm run lint from 5 errors to 0 (warnings unchanged at 44).
Each disable is the smallest local suppression with a one-line reason
on the same comment, so the next reader sees why the rule was muted:
- src/components/ui/textarea.tsx (line 79): the effect intentionally
syncs the internal auto-size trigger with the controlled value prop
every time the parent changes it.
- src/features/flows/files/use-flow-container-files.ts (line 107): the
effect calls an async fetcher (fetchListing) whose setState runs
after await — not synchronously inside the effect body; pathsKey is
intentionally used in place of the paths array reference.
- src/pages/settings/settings-prompt.tsx (line 344): the useMemo
branches on data.settingsPrompts which the react-compiler can't
statically prove stable.
- src/providers/resources-provider.tsx (line 70): intentional
mount-time loading flag for the REST hydration path.
- src/providers/user-provider.tsx (line 144): refreshAuthInfo's
setState runs after an async /auth/info fetch, not synchronously.
Build, 475/475 vitest tests, and runtime smoke (dashboard, flows,
knowledges, settings/providers) all pass.
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>
- Introduced `EMBEDDING_MAX_TEXT_BYTES` to limit the maximum byte size of text sent to the embedding model.
- Renamed database connection pool settings: `DATABASE_MAX_OPEN_CONNS`, `DATABASE_MAX_IDLE_CONNS`, and `DATABASE_VECTOR_MAX_CONNS` for improved PostgreSQL connection management.
- Updated relevant documentation to reflect these new configuration options and their usage.
- Adjusted various components to utilize the new settings for enhanced performance and resource management.
- Added ToolCallLogProvider interface with methods for logging tool calls, updating success and failure statuses.
- Introduced proxyToolCallLogProvider to handle ToolCall logging operations.
- Updated flow execution components to integrate ToolCall logging, including flow workers and controllers.
- Enhanced GraphQL schema to support ToolCall logs, including queries and subscriptions for real-time updates.
- Updated documentation to reflect the new ToolCall logging features and their usage.
- Introduced shared connection pooling for PostgreSQL using `*sql.DB` for sqlc and GORM, optimizing resource usage.
- Added new environment variables: `DB_MAX_OPEN_CONNS`, `DB_MAX_IDLE_CONNS`, and `DB_VECTOR_MAX_CONNS` for configurable connection limits.
- Updated documentation to reflect new connection pooling strategy and provide operational commands for monitoring.
- Implemented shared `pgxpool` for pgvector stores to reduce connection overhead and improve performance.
- Adjusted various components to utilize the new connection pooling setup, ensuring efficient database interactions.
- Introduced WaitTaskCompletion method in FlowWorker interface to block until the current task completes or the context expires.
- Implemented signalTaskComplete to manage task completion signaling across goroutines.
- Added waitFlowCompletion tool to handle waiting for task completion with configurable timeout.
- Updated assistant provider to include wait functionality for flow completion.
- Enhanced templates and tool registry to support new wait functionality.
Gated behind the `anonymize.call` privilege from `useUser()`; runs the
current `content` through the `anonymizeText` GraphQL mutation and writes
the result back. Desktop renders the button before Save; mobile collapses
it into the first item of the actions dropdown (the dropdown now appears
for new documents too when the privilege is present).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Apollo client's global nextFetchPolicy is 'cache-first', so returning
to /knowledges via SPA navigation would serve potentially stale cache.
Subscriptions are now scoped to /knowledges* and detach on other pages,
so changes from AI agents during flow runs would be missed until a full
reload. Override nextFetchPolicy to keep cache-and-network for this query.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
KnowledgesProvider sat in RootLayout, so knowledgeDocuments (with full
content) and its 3 subscriptions fired on every authenticated page —
a ~2.1 MB payload on /flows, /dashboard, etc. All consumers of
useKnowledges live under /knowledges*, so wrap those routes in a
dedicated layout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A paste of multi-KB content into the filter would land in the URL
verbatim (`?q=` plus the raw blob), which exceeds the practical
reverse-proxy limit (~2–4 KB) and breaks the share-link experience.
The user-facing entry point is a single `<input>`, so a DOM-level
`maxLength` is sufficient — it truncates both typing and paste before
the value ever reaches React state, the URL, or localStorage. 200 chars
is well above any realistic search term while leaving plenty of
headroom for non-ASCII expansion under percent-encoding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The split `useTableQueryFilter` + `usePagination` pair had a latent batching
race: react-router v6 feeds every functional `setSearchParams(updater)`
queued in a single React tick the same pre-batch snapshot, so a `setFilter`
+ `setPage` issued from the same event handler (e.g. a debounced filter
commit landing alongside a paging click) would collapse — the second
write erased the first and `q` disappeared from the URL. The earlier
window.location workaround papered over the symptom for the typical case
but didn't remove the underlying possibility.
Replace both hooks with `useTableState`, which owns filter + pageIndex +
storage roundtrip together and routes every URL write through a single
microtask-coalesced `update(patch)`. When multiple `update` calls fire in
the same tick — `setFilter` and `setPage`, two synchronous handlers, an
effect and a click — they merge into one navigation rather than racing.
Replace conflicts resolve in favour of push, so intentional history
entries (paging) survive coalescence with replace-only updates (filter
typing). The `MemoryRouter` fallback (latest snapshot via ref) is kept
only as a defensive read; the coalescence itself makes it redundant.
A regression test (`two top-level updaters firing in the same tick keep
both params`) locks this in: the previous behaviour failed it, the new
implementation passes it without touching `window.location`.
Read-only siblings stay on `useTableQueryFilterReader` — detail pages
don't write the URL, so no race surface to remove. `usePagination`
deleted entirely; its callers migrated to `useTableState`.
474/474 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a debounced filter commit and a paging button click land in the same
React tick, react-router v6 feeds the functional updater of every queued
`setSearchParams` call the same pre-batch snapshot. The second write
overwrites the first instead of stacking, so `setFilter('foo')` followed
by `setPage(5)` collapses to `?page=6` and `q` is lost. The QA pass
caught this on /flows: clicking >> right after typing in the filter
silently dropped `?q=` from the share-able URL.
Build the next `URLSearchParams` from `window.location.search` instead,
since that's the freshest source of truth under BrowserRouter and
sidesteps react-router's batching entirely. Fall back to the latest
react-router snapshot (stashed in a render-synced ref) under
MemoryRouter, which the hook tests use — `MemoryRouter` doesn't sync
its in-memory history to `window.location`.
478/478 existing tests still pass; the browser repro that previously
produced `?page=6` now correctly produces `?q=bypass&page=6`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hard-coding the full `table_4_/settings/prompts:agents` / `:tools` keys in
the page duplicated the route prefix that `usePageStorageKeys` already
computes — if the prefix ever bumps (e.g. `table_4_` → `table_5_` on a
storage migration), every call site silently goes stale. Read the route
base from the hook instead and only own the per-table suffix in the page.
Updated the `storageKey` JSDoc on DataTable to document this composition
pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Capture the QA pass we did against the multi-column search rollout:
INP baselines per page, the four bugs we hit (two fixed in the
follow-up commit, two left as backlog), the negative results from
the adversarial input pass (XSS / SQLi / regex meta / unicode /
prototype pollution via localStorage), and the regression scenarios
(S1–S11) so the next round of changes has a concrete checklist
instead of "run it and see if it feels OK".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two issues uncovered by the QA pass over /settings/prompts, where one page
mounts two DataTable instances:
1. Both inputs shared a hard-coded `id="data-table-search"` and
`name="search"`. `document.getElementById` only ever found the first
one, screen readers couldn't disambiguate the two filter inputs, and
any test selector keyed on the id silently picked the wrong field.
Switch to `useId()` so the id and name are per-instance, non-empty,
and stable across renders.
2. Both tables persisted sorting / column visibility / search-column
narrowing into the same `table_4_/settings/prompts` slot. The last
writer won the race; on reload the loser inherited the winner's
state. Add a `storageKey?: string` prop to DataTable (mirroring the
existing `useTableQueryFilter` API) and pass `…:agents` / `…:tools`
for the two tables on /settings/prompts so they get distinct slots.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
For both the Agent Prompts and Tool Prompts tables, mark every accessor
column with `meta.searchable: true` (name + status fields) and drop the
explicit `filterColumn="displayName"`. Typing "Custom" now surfaces
every prompt currently overridden — a much more useful starting point
than scanning the column visually. Added `columnMenuLabel` overrides
where the underlying accessorKey wasn't human-readable on its own.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mark `name`, `tokenId`, and `status` with `meta.searchable: true` and
drop the explicit `filterColumn="name"`. The bigger wins are tokenId
(operators often paste a fragment from logs or a request header to find
the owning token) and status (typing "revoked" surfaces every revoked
token without scrolling). Date columns stay excluded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mark `name` and `type` with `meta.searchable: true` and drop the
explicit `filterColumn="name"` so users can also narrow by provider
type — typing "anthropic" surfaces every provider of that type without
having to remember its display name. Updated the placeholder to match
the broader scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mark both accessor columns with `meta.searchable: true` and drop the
explicit `filterColumn="title"` so the picker offers both fields and
OR-matches across them. Searching the body text is the bigger win —
template titles are short, so the previous single-column behaviour
missed templates that referenced the same topic in their body.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mark `docType`, `question`, and `content` with `meta.searchable: true` so
the multi-column picker offers all three as candidates and OR-matches
across them. Drop the explicit `filterColumn="question"` so the
zero-config path (search across all `meta.searchable` columns) takes
over. The `Flags` column has no accessor and would need a custom string
extractor to participate — skipped for now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch the `provider` and `terminals` columns from `accessorKey` to
`accessorFn` so the global filter receives plain strings — the provider
name and the joined list of terminal images — instead of the raw object
or array, which the predicate would just stringify to `[object Object]`.
The cell renderers keep reading `row.original` directly, so the visible
output is unchanged.
Dates stay excluded: substring matching against formatted timestamps
gives unpredictable hits (e.g. typing "3" suddenly matches every row
whose ISO contains 03) and date filtering belongs in a range picker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Typing into the Flows search felt sluggish — Chrome reported INP 271 ms with input delay 253 ms, meaning every keystroke had to wait for the previous keystroke's render to complete before the event handler could even run. The chain on every keypress was: `setSearchParams` → react-router rerenders the whole route subtree → TanStack re-derives the global filter → 91 rows × 3 predicate calls → reconcile ~70 cells (Tooltip + Badge + ProviderIcon). At ~250 ms per cycle a fast typist queues several keystrokes behind a running render, so the input appeared to lag visibly.
Split the input value from the upstream commit: `DataTableFilter` now owns a local string state that updates synchronously on every keystroke, while a 150 ms debounce mirrors the value into `onQueryChange`. The router / TanStack cascade now fires once per typing pause instead of once per keystroke, and the input never has to wait its turn behind an in-flight reconciliation. After the change Chrome reports INP 43 ms with input delay 2 ms — typing feels instant.
Externally visible behaviour is unchanged: the X button clears immediately (no debounce on explicit clears), and external `query` resets (back button, programmatic clear) sync down through a guarded effect that ignores the value we just emitted ourselves. Vitest tests that previously asserted against the synchronous commit now wait for the debounced row-model update via `findBy*` / `waitFor`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`getColumnId` only has one consumer — `lib/` is for utilities shared across the codebase, not for a single-call helper. Move it next to `columnPickerLabel` as a module-level function inside `data-table.tsx`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>