a59ed804226e50adea45e218d91850f0d4cd897f
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1833fa84a5 |
Fix front component pointer/mouse event coordinates (#21117)
Fixes https://github.com/twentyhq/twenty/issues/21000 Front-component event handlers read standard event fields (event.clientX, event.offsetX, …), but these were always undefined. On the remote side, serialized event data was passed only as the CustomEvent's detail — and CustomEvent ignores every constructor option except detail, so the values lived at event.detail.clientX and never on the event object itself. - Added `applySerializedEventProperties`, to copy a curated allowlist of event-level keys onto the event. Element/target state (value, checked, files, scroll, media props) stays in `applySerializedEventTargetProperties`, applied to this (the dispatch element = event.target). - Added x/y to `SerializedEventData` and to host-side serialization in `createHtmlHostWrapper`. - Added an `svg-pointer `story + `createHtmlTagPointerStory` Note: Also pinned @types/react to v18 so the renderer stops dragging in React 19 types and breaking typecheck. |
||
|
|
563acc3f57 |
Allow copy to clipboard and pointer/mousemove events in front components (#20858)
Follow-up to #20525, picks up the clipboard + mouse/pointer events asks from the "Allow to copy to clipboard in front-component" Slack thread. `navigator.geolocation` and `getBoundingClientRect` are intentionally out of scope until we have a permission model. ### `copyToClipboard` host API New SDK function `copyToClipboard` (in `twenty-sdk/front-component`) that goes through the host bridge to `useCopyToClipboard` in `twenty-front`: ```ts import { copyToClipboard } from 'twenty-sdk/front-component'; await copyToClipboard('hello'); ``` Host-side hardening (front-component code is untrusted): - Drops anything that isn't a non-empty string - Caps payload at 64KB - Throttles to 1 call/sec per front-component instance - Snackbar shows a truncated preview so the user can spot a mismatch between the affordance they clicked and what actually got copied ### `mousemove` and pointer events Added to `COMMON_HTML_EVENTS` (and the React mapping) so they fire on every HTML tag the renderer ships: `mousemove`, `pointerdown/up/move`, `pointerover/out/enter/leave/cancel`. Generator rerun for `remote-elements.ts` and `remote-components.ts`. `SerializedEventData` now also forwards pointer geometry: `pointerId`, `pointerType`, `pressure`, `tangentialPressure`, `tiltX/Y`, `twist`, `width/height`, `isPrimary`. Existing positional fields are unchanged. ### Coverage - New Storybook stories: `HostApi/CopyToClipboard` and `HtmlTag/Grouping/Div/Events::PointerMove` - `useFrontComponentExecutionContext` unit tests cover the API call, preview truncation, type guard, length cap, and rate limit - Renderer Storybook suite 227 → 229, prebuild bundle count 219 → 221 |
||
|
|
e3c79c803c |
Fix standard React form event targets in front components (#20525)
Fixes #20354 ## Problem Front component form events currently expose serialized form state through a sandbox-specific event shape, such as `event.detail.value` and `event.detail.checked`. That works for examples that explicitly read `event.detail`, but it is surprising for app authors writing standard React form handlers: ```tsx onChange={(event) => { setValue(event.target.value); }} Internal app code already has to defend against multiple possible shapes: // Values may live on e.detail.value, e.value, or e.target.value. This suggests the sandbox event shape is leaking into userland. Solution This change keeps the existing event.detail behavior, but also syncs serialized event target properties back onto the remote element before dispatching the event. That means both styles work: // Existing sandbox-specific style event.detail.value; // Standard React style event.target.value; The same applies to checked, files, scroll/media target properties, and similar serialized target state. What Changed Added a shared helper to apply serialized event target properties onto the remote element. Updated generated remote element event configs to dispatch serialized events through a custom event config. Updated the remote-dom element generator so regenerated files preserve this behavior. Updated Storybook form-event examples to use standard React event target reads. Added/updated Storybook coverage for input, checkbox, textarea, select, submit, and caret preservation flows. Validation Ran git diff --check Ran a targeted TypeScript error scan for the changed front component renderer files Manually verified the Storybook FrontComponent/EventForwarding form event story locally: text input updates state checkbox updates state submit reflects the updated JSON Note: local Storybook verification on Windows required temporary local build/cache fixes that are not included in this PR, to keep this change focused on front component event behavior. --------- Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
dea1f89904 |
Inject none secret env variables into front components (#20511)
## Summary - Inject non-secret application variables (`isSecret: false`) into front component `process.env` via the existing Web Worker `setWorkerEnv` mechanism - Filter secret variables server-side in the resolver so they never reach the browser - Set application variables before system variables (`TWENTY_API_URL`, `TWENTY_APP_ACCESS_TOKEN`) to prevent override - Wire up environment variable keys in the logic function code editor for TypeScript autocomplete ## Test plan - [x] Unit tests for `buildNonSecretEnvVar` (6 passing) - [x] Typecheck passes for `twenty-front` and `twenty-server` - [x] Install an app with both `isSecret: false` and `isSecret: true` variables, open a front component, verify only non-secret vars appear in `process.env` - [x] Open a logic function editor, verify autocomplete suggests declared variable keys |
||
|
|
75c22a2119 |
feat(front-component-renderer): forward file input metadata (#20458)
## Summary `<input type=\"file\">` inside front-components was silently non-functional: - The host-side `serializeEvent` did not read `target.files`, so the worker received an empty `onChange` detail. - `SerializedEventData` had no `files` field. - The `html-input` schema in `AllowedHtmlElements` exposed neither `accept`, `multiple`, nor `capture` — the worker could not even configure the picker. This PR forwards file metadata (`name`, `size`, `type`, `lastModified`) through the existing serialized event detail and accepts the missing attributes on the `html-input` remote element. A new Storybook play test guards the regression by uploading single and multiple files via `userEvent.upload`. Reading file contents inside the worker is intentionally out of scope here and will need a separate host API bridge (the host has the `File` objects on the real input element; passing bytes through `postMessage` is a bigger design call). |
||
|
|
eb1ca1b9ec |
perf(sdk): split twenty-sdk barrel into per-purpose subpaths to cut logic-function bundle ~700x (#19834)
## Summary
Logic-function bundles produced by the twenty-sdk CLI were ~1.18 MB even
for a one-line handler. Root cause: the SDK shipped as a single bundled
barrel (`twenty-sdk` → `dist/index.mjs`) that co-mingled server-side
definition factories with the front-component runtime, validation (zod),
and React. With no `\"sideEffects\"` declaration on the SDK package,
esbuild had to assume every module-level statement could have side
effects and refused to drop unused code.
This PR restructures the SDK so consumers' bundlers can tree-shake at
the leaf level:
- **Reorganized SDK source.** All server-side definition factories now
live under `src/sdk/define/` (agents, application, fields,
logic-functions, objects, page-layouts, roles, skills, views,
navigation-menu-items, etc.). All front-component runtime
(components, hooks, host APIs, command primitives) lives under
`src/sdk/front-component/`. The legacy bare `src/sdk/index.ts` is
removed; the bare `twenty-sdk` entry no longer exists.
- **Split the build configs by purpose / runtime env.** Replaced
`vite.config.sdk.ts` with two purpose-specific configs:
- `vite.config.define.ts` — node target, externals from package
`dependencies`, emits to `dist/define/**`
- `vite.config.front-component.ts` — browser/React target, emits to
`dist/front-component/**`
Both use `preserveModules: true` so each leaf ships as its own `.mjs`.
- **\`\"sideEffects\": false\`** on `twenty-sdk` so esbuild can drop
unreferenced re-exports.
- **\`package.json\` exports + \`typesVersions\`** updated: dropped the
bare \`.\` entry, added \`./front-component\`, and pointed \`./define\`
at the new per-module dist layout.
- **Migrated every internal/example/community app** to the new subpath
imports (`twenty-sdk/define`, `twenty-sdk/front-component`,
`twenty-sdk/ui`).
- **Added \`bundle-investigation\` internal app** that reproduces the
bundle bloat and demonstrates the fix.
- Cleaned up dead \`twenty-sdk/dist/sdk/...\` references in the
front-component story builder, the call-recording app, and the SDK
tsconfig.
## Bundle size impact
Measured with esbuild using the same options as the SDK CLI
(\`packages/twenty-apps/internal/bundle-investigation\`):
| Variant | Imports | Before | After |
| ----------------------- |
------------------------------------------------------- | ---------- |
--------- |
| \`01-bare\` | \`defineLogicFunction\` from \`twenty-sdk/define\` |
1177 KB | **1.6 KB** |
| \`02-with-sdk-client\` | + \`CoreApiClient\` from
\`twenty-client-sdk/core\` | 1177 KB | **1.9 KB** |
| \`03-fetch-issues\` | + GitHub GraphQL fetch + JWT signing + 2
mutations | 1181 KB | **5.8 KB** |
| \`05-via-define-subpath\` | same as \`01\`, via the public subpath |
1177 KB | **1.7 KB** |
That's a ~735× reduction on the bare baseline. Knock-on benefits for
Lambda warm + cold starts, S3 upload size, and \`/tmp\` disk usage in
warm containers.
## Test plan
- [x] \`npx nx run twenty-sdk:build\` succeeds
- [x] \`npx nx run twenty-sdk:typecheck\` passes
- [x] \`npx nx run twenty-sdk:test:unit\` passes (31 files / 257 tests)
- [x] \`npx nx run-many -t typecheck
--projects=twenty-front,twenty-server,twenty-front-component-renderer,twenty-sdk,twenty-shared,bundle-investigation\`
passes
- [x] \`node
packages/twenty-apps/internal/bundle-investigation/scripts/build-variants.mjs\`
produces the sizes above
- [ ] CI green
Made with [Cursor](https://cursor.com)
|
||
|
|
7ba5fe32f8 |
Add new html tags to the remote elements (#19723)
- Add 72 missing HTML and SVG elements to the remote-dom component registry (48 HTML + 24 SVG), bringing the total from 47 to 119 supported elements - HTML additions include semantic inline text (b, i, u, s, mark, sub, sup, kbd, etc.), description lists, ruby annotations, structural elements (figure, details, dialog), and form utilities (fieldset, progress, meter, optgroup) - SVG additions include containers (svg, g, defs), shapes (path, circle, rect, line, polygon), text (text, tspan), gradients (linearGradient, radialGradient, stop), and utilities (clipPath, mask, foreignObject, marker) - Add htmlTag override to support SVG elements with camelCase names (e.g. clipPath, foreignObject) while keeping custom element tags lowercase per the Web Components spec |
||
|
|
16033e9f99 |
Fix front component worker re-creation on every render (#19245)
- `frontComponentHostCommunicationApi` gets a new object reference on every render, causing the `useMemo`/`useEffect` in `FrontComponentWorkerEffect` to tear down and re-create the web worker each time. - Decouple the host API lifecycle from the worker lifecycle by moving thread.exports updates into a dedicated `FrontComponentUpdateHostCommunicationApiEffect` that mutates the thread's exports object in place via Object.assign. - Rename `FrontComponentHostCommunicationApiEffect` to `FrontComponentInitializeHostCommunicationApiEffect` for clarity. ## Before https://github.com/user-attachments/assets/6f3a5c14-2ae7-4317-82b5-1625abb4143e ## After https://github.com/user-attachments/assets/1059d6cd-e02c-4477-b3e8-8e965a716434 |
||
|
|
37908114fc |
[SDK] Extract twenty-front-component-renderer outside of twenty-sdk ( 2.8MB ) (#19021)
Followup https://github.com/twentyhq/twenty/pull/19010 ## Dependency diagram ``` ┌─────────────────────┐ │ twenty-front │ │ (React frontend) │ └─────────┬───────────┘ │ imports runtime: │ FrontComponentRenderer │ FrontComponentRendererWithSdkClient │ useFrontComponentExecutionContext ▼ ┌──────────────────────────────────┐ ┌─────────────────────────┐ │ twenty-front-component-renderer │────────▶│ twenty-sdk │ │ (remote-dom host + worker) │ │ (app developer SDK) │ │ │ │ │ │ imports from twenty-sdk: │ │ Public API: │ │ • types only: │ │ defineFrontComponent │ │ FrontComponentExecutionContext│ │ navigate, closeSide… │ │ NavigateFunction │ │ useFrontComponent… │ │ CloseSidePanelFunction │ │ Command components │ │ CommandConfirmation… │ │ conditional avail. │ │ OpenCommandConfirmation… │ │ │ │ EnqueueSnackbarFunction │ │ Internal only: │ │ etc. │ │ frontComponentHost… │ │ │ │ front-component-build │ │ owns locally: │ │ esbuild plugins │ │ • ALLOWED_HTML_ELEMENTS │ │ │ │ • EVENT_TO_REACT │ └────────────┬────────────┘ │ • HTML_TAG_TO_CUSTOM_ELEMENT… │ │ │ • SerializedEventData │ │ types │ • PropertySchema │ ▼ │ • frontComponentHostComm… │ ┌─────────────────────────┐ │ (local ref to globalThis) │ │ twenty-shared │ │ • setFrontComponentExecution… │ │ (common types/utils) │ │ (local impl, same keys) │ │ AppPath, SidePanelP… │ │ │ │ EnqueueSnackbarParams │ └──────────────────────────────────┘ │ isDefined, … │ │ └─────────────────────────┘ │ also depends on ▼ twenty-shared (types) @remote-dom/* (runtime) @quilted/threads (runtime) react (runtime) ``` **Key points:** - **`twenty-front`** depends on the renderer, **not** on `twenty-sdk` directly (for rendering) - **`twenty-front-component-renderer`** depends on `twenty-sdk` for **types only** (function signatures, `FrontComponentExecutionContext`). The runtime bridge (`frontComponentHostCommunicationApi`) is shared via `globalThis` keys, not module imports - **`twenty-sdk`** has no dependency on the renderer — clean one-way dependency - The renderer owns all remote-dom infrastructure (element schemas, event mappings, custom element tags) that was previously leaking through the SDK's public API - The SDK's `./build` entry point was removed entirely (unused) |