8da69e0f77ea820a6845a4c3c025b6af3861d523
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7b6fb52df7 |
fix: validate blocknote JSON in rich text fields (#18902)
## Summary - **Backend**: Add JSON validation for the `blocknote` subfield in rich text API inputs — rejects values that aren't valid JSON or aren't arrays (BlockNote content is always `PartialBlock[]`). This prevents corrupted data from being persisted to the database. - **Frontend**: Replace all 5 unprotected `JSON.parse` calls on blocknote content with the safe `parseJson` utility from `twenty-shared`. Invalid content now degrades gracefully (empty block / empty string / unchanged passthrough) instead of crashing the app. - **Tests**: Added integration tests for invalid blocknote JSON (both GraphQL and REST), unit tests for the new validation, and updated existing test constants to use valid BlockNote JSON. ## Context A user reported a `SyntaxError: Expected ',' or ']' after array element` crash caused by malformed blocknote JSON stored in the database. The data had `"children":[]` nested inside the `content` array instead of as a sibling property. The API accepted this invalid JSON because it only validated that `blocknote` was a string, not that it contained valid JSON. On the frontend, 5 call sites used bare `JSON.parse` with no error handling, causing a white-screen crash. ## Test plan - [x] Unit tests pass: `validate-rich-text-field-or-throw.util.spec.ts` (10/10) - [x] Integration tests pass: `rich-text-field-create-input-validation` (8/8) - [ ] Verify creating a note with valid rich text still works end-to-end - [ ] Verify API returns clear error when blocknote contains invalid JSON - [ ] Verify frontend renders empty block instead of crashing when encountering corrupted data 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
602db4ffea |
feat: enable Rich Text as a creatable field type (#18634)
## Summary - Removes `RICH_TEXT` from the excluded/hidden field types in the settings UI so users can create rich text fields on any object (not just Note/Task) - Creates a generic `RichTextFieldEditor` component that uses standard `useUpdateOneRecord` for persistence, decoupled from the Note/Task-specific `ActivityRichTextEditor` - Updates the inline `RichTextFieldInput` and side panel to route to the appropriate editor based on object type (activity editor for Note/Task, generic editor for everything else) ## Details ### Tier 1 — Settings UI unlock - Removed `RICH_TEXT` from `excludedFieldTypes` in `SettingsObjectNewFieldSelect.tsx` - Removed `RICH_TEXT` from `SettingsExcludedFieldType` type union - Added `RICH_TEXT` to `previewableTypes` in `SettingsDataModelFieldSettingsFormCard` ### Tier 2 — Generic inline editing - New `RichTextFieldEditor` — a generic BlockNote editor that works for any object using `useUpdateOneRecord` (no activity-specific coupling) - `RichTextFieldInput` now branches: `ActivityRichTextEditor` for Note/Task, `RichTextFieldEditor` for all other objects - Generalized side panel state (`viewableRichTextComponentState`) from `activityId`/`activityObjectNameSingular` to `recordId`/`objectNameSingular`/`fieldName` - `useOpenRichTextInSidePanel` now accepts an optional `fieldName` parameter ### Tier 3 — Verification - Search: only `markdown` subfield is indexed (correct behavior) - Filters: `RichTextFilter` GraphQL input type already exists - Import/export: `markdown` subfield is already marked `isImportable: true` |
||
|
|
9d57bc39e5 |
Migrate from ESLint to OxLint (#18443)
## Summary Fully replaces ESLint with OxLint across the entire monorepo: - **Replaced all ESLint configs** (`eslint.config.mjs`) with OxLint configs (`.oxlintrc.json`) for every package: `twenty-front`, `twenty-server`, `twenty-emails`, `twenty-ui`, `twenty-shared`, `twenty-sdk`, `twenty-zapier`, `twenty-docs`, `twenty-website`, `twenty-apps/*`, `create-twenty-app` - **Migrated custom lint rules** from ESLint plugin format to OxLint JS plugin system (`@oxlint/plugins`), including `styled-components-prefixed-with-styled`, `no-hardcoded-colors`, `sort-css-properties-alphabetically`, `graphql-resolvers-should-be-guarded`, `rest-api-methods-should-be-guarded`, `max-consts-per-file`, and Jotai-related rules - **Migrated custom rule tests** from ESLint `RuleTester` + Jest to `oxlint/plugins-dev` `RuleTester` + Vitest - **Removed all ESLint dependencies** from `package.json` files and regenerated lockfiles - **Updated Nx targets** (`lint`, `lint:diff-with-main`, `fmt`) in `nx.json` and per-project `project.json` to use `oxlint` commands with proper `dependsOn` for plugin builds - **Updated CI workflows** (`.github/workflows/ci-*.yaml`) — no more ESLint executor - **Updated IDE setup**: replaced `dbaeumer.vscode-eslint` with `oxc.oxc-vscode` extension, configured `source.fixAll.oxc` and format-on-save with Prettier - **Replaced all `eslint-disable` comments** with `oxlint-disable` equivalents across the codebase - **Updated docs** (`twenty-docs`) to reference OxLint instead of ESLint - **Renamed** `twenty-eslint-rules` package to `twenty-oxlint-rules` ### Temporarily disabled rules (tracked in `OXLINT_MIGRATION_TODO.md`) | Rule | Package | Violations | Auto-fixable | |------|---------|-----------|-------------| | `twenty/sort-css-properties-alphabetically` | twenty-front | 578 | Yes | | `typescript/consistent-type-imports` | twenty-server | 3814 | Yes | | `twenty/max-consts-per-file` | twenty-server | 94 | No | ### Dropped plugins (no OxLint equivalent) `eslint-plugin-project-structure`, `lingui/*`, `@stylistic/*`, `import/order`, `prefer-arrow/prefer-arrow-functions`, `eslint-plugin-mdx`, `@next/eslint-plugin-next`, `eslint-plugin-storybook`, `eslint-plugin-react-refresh`. Partial coverage for `jsx-a11y` and `unused-imports`. ### Additional fixes (pre-existing issues exposed by merge) - Fixed `EmailThreadPreview.tsx` broken import from main rename (`useOpenEmailThreadInSidePanel`) - Restored truthiness guard in `getActivityTargetObjectRecords.ts` - Fixed `AgentTurnResolver` return types to match entity (virtual `fileMediaType`/`fileUrl` are resolved via `@ResolveField()`) ## Test plan - [x] `npx nx lint twenty-front` passes - [x] `npx nx lint twenty-server` passes - [x] `npx nx lint twenty-docs` passes - [x] Custom oxlint rules validated with Vitest: `npx nx test twenty-oxlint-rules` - [x] `npx nx typecheck twenty-front` passes - [x] `npx nx typecheck twenty-server` passes - [x] CI workflows trigger correctly with `dependsOn: ["twenty-oxlint-rules:build"]` - [x] IDE linting works with `oxc.oxc-vscode` extension |
||
|
|
4ed09a3feb |
Upgrade blocknote dependencies from 0.31.1 to 0.47.0. (#18207)
This PR pgrades all BlockNote packages (@blocknote/core, @blocknote/react, @blocknote/mantine, @blocknote/server-util, @blocknote/xl-docx-exporter, @blocknote/xl-pdf-exporter) to 0.47.0 and adapts the codebase to the new API. ### Changes - Dependency upgrades: Bumped all BlockNote packages to 0.47.0, added required Mantine v8 peer dependencies, removed unnecessary prosemirror resolutions - Formatting toolbar: Replaced the manual reimplementation of FormattingToolbarController (which handled visibility, positioning, portal rendering, text-alignment-based placement, and a dangerouslySetInnerHTML transition trick) with BlockNote's built-in FormattingToolbarController. The toolbar buttons themselves are unchanged. - Side menu: Replaced manual drag handle menu positioning and rendering (DashboardBlockDragHandleMenu, DashboardBlockColorPicker, and their floating configs) with BlockNote's built-in SideMenuController, DragHandleButton, and DragHandleMenu components. Deleted 4 files that became dead code. - Extension API migration: Replaced deprecated editor.suggestionMenus and editor.formattingToolbar APIs with the new extension system (SuggestionMenu, useExtensionState, editor.getExtension()) - Slash menu fixes: Filtered out BlockNote's new default "File" item (added in 0.47) to avoid duplicates with our custom one; added icon mappings for new block types (Toggle List, Divider, Toggle Headings, Headings 4-6) - Server-side: Switched @blocknote/server-util to dynamic import() to handle ESM-only transitive dependencies in CJS context |
||
|
|
6f251a6f8e |
Add @mention support in AI Chat input (#17943)
## Summary
- Add `@mention` support to the AI Chat text input by replacing the
plain textarea with a minimal Tiptap editor and building a shared
`mention` module with reusable Tiptap extensions (`MentionTag`,
`MentionSuggestion`), search hook (`useMentionSearch`), and suggestion
menu — all shared with the existing BlockNote-based Notes mentions to
avoid code duplication
- Mentions are serialized as
`[[record:objectName:recordId:displayName]]` markdown (the format
already understood by the backend and rendered in chat messages), and
displayed using the existing `RecordLink` chip component for visual
consistency
- Fix images in chat messages overflowing their container by
constraining to `max-width: 100%`
- Fix web_search tool display showing literal `{query}` instead of the
actual query (ICU single-quote escaping issue in Lingui `t` tagged
templates)
## Test plan
- [ ] Open AI Chat, type `@` and verify the suggestion menu appears with
searchable records
- [ ] Select a mention from the dropdown (via click or keyboard
Enter/ArrowUp/Down) and verify the record chip renders inline
- [ ] Send a message containing a mention and verify it appears
correctly in the conversation as a clickable `RecordLink`
- [ ] Verify Enter sends the message when the suggestion menu is closed,
and selects a mention when the menu is open
- [ ] Verify images in AI chat responses are constrained to the
container width
- [ ] Verify the web_search tool step shows the actual search query
(e.g. "Searched the web for Salesforce") instead of `{query}`
- [ ] Verify Notes @mentions still work as before
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
|