fix(ui): stop RoundedLink labels clipping in table cells#21413
fix(ui): stop RoundedLink labels clipping in table cells#21413bcharleson wants to merge 13 commits into
Conversation
Remove Twenty branding fallbacks on auth and navigation, document fork management and upstream sync, and add weekly upstream PR plus GHCR publish workflows for the TOFU CRM image. Co-authored-by: Cursor <cursoragent@cursor.com>
Use stable v3/v6 action tags so the tofu-twenty image build can run on tag push. Co-authored-by: Cursor <cursoragent@cursor.com>
Inject workspace logo into HTML for crawlers, redirect /favicon.ico to the signed logo URL, and extend PageFavicon for PWA manifest overrides so Slack and browser unfurls no longer show Twenty's default icon. Co-authored-by: Cursor <cursoragent@cursor.com>
twenty-shared/utils does not re-export this guard; use the same import pattern as other twenty-front modules so the Docker build succeeds. Co-authored-by: Cursor <cursoragent@cursor.com>
Restores server startup after workspace branding middleware was added. Co-authored-by: Cursor <cursoragent@cursor.com>
Serve SPA index through branding middleware, use stable /favicon.ico for tab and install icons, slim static manifest fallback, and document upstream merge checklist in tofu/WHITE-LABEL.md. Co-authored-by: Cursor <cursoragent@cursor.com>
Use a shared front dist path in Docker, resolve branding from SERVER_URL when proxy headers are incomplete, handle /favicon.ico in middleware, and point PageFavicon at the signed workspace logo URL. Co-authored-by: Cursor <cursoragent@cursor.com>
Import isNonEmptyString from @sniptt/guards instead of twenty-shared/utils so favicon and branded index HTML resolve correctly in production. Co-authored-by: Cursor <cursoragent@cursor.com>
Serve /manifest.json from workspace branding middleware so install prompts and fresh loads stay white-labeled, with a generic SVG fallback when no workspace logo is configured. Co-authored-by: Cursor <cursoragent@cursor.com>
Redirect /favicon.ico to the neutral default icon when no workspace logo is set, detect logo format in generated manifests, and skip upstream CD workflows that require TWENTY_INFRA_TOKEN on the TOFU fork. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Merge upstream/main (2.11.0) into TOFU fork. Resolves conflicts in cd-deploy-tag.yaml, ImageInput.tsx, and SignInUp.tsx while preserving white-label patches and the upstream-only deploy guard.
The link chip used a 10px content height with overflow hidden, which cut off domain labels in dense table views. Match Chip small sizing: border-box, spacing-3 height, sm font size, and centered flex alignment. Co-authored-by: Cursor <cursoragent@cursor.com>
|
👋 Thanks for contributing to Twenty! We're excited to have you on board. Your PR has been set to draft while you work on it. Once you're done, mark it as Ready for review and our automated checks will run. By submitting your Pull Request, you acknowledge that you agree with the terms of our Contributor License Agreement. |
Welcome!
Hello there, congrats on your first PR! We're excited to have you contributing to this project. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Introduces a TOFU-specific white-labeling and ops playbook for a Twenty CRM fork, including server-side first-paint branding (favicon/manifest/HTML head injection) and GitHub automation for upstream syncing + image publishing.
Changes:
- Added extensive TOFU documentation for deployment, pricing, white-label requirements, and upstream sync procedures.
- Implemented server-side workspace branding (dynamic
/manifest.json,/favicon.ico, and branded SPA HTML on first load) plus related frontend white-label tweaks. - Added GitHub workflows for weekly upstream sync PRs and GHCR Docker image publishing for TOFU tags.
Reviewed changes
Copilot reviewed 45 out of 46 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tofu/partnerships/twenty-crm/company-profile.md | Partnership research brief for Twenty |
| tofu/WHITE-LABEL.md | White-label patch inventory + verification checklist |
| tofu/UPSTREAM-SYNC.md | Describes weekly upstream sync PR workflow |
| tofu/SETUP.md | Local/dev/Compose setup paths and troubleshooting |
| tofu/README.md | Fork rationale, licensing notes, and doc index |
| tofu/PRICING.md | Internal cost model + client pricing tiers |
| tofu/FORK-MANAGEMENT.md | Fork strategy, patch tracking, and merge playbook |
| tofu/DEPLOYMENT-DIGITALOCEAN.md | Canonical DO App Platform + Supabase + Upstash deployment guide |
| tofu/DATABASE.md | Postgres/Redis/storage architecture and Supabase wiring guidance |
| tofu/CLIENT-CUSTOMIZATION.md | Step-by-step client onboarding/customization checklist |
| tofu/BRANDING.md | Branding/asset map and white-labeling guidance |
| packages/twenty-ui-deprecated/src/navigation/link/components/RoundedLink.tsx | Adjusts link styling to align with new sizing/typography |
| packages/twenty-server/src/utils/generate-front-config.ts | Uses shared dist-path resolver; skips config injection when front dist isn’t present |
| packages/twenty-server/src/engine/core-modules/workspace-branding/workspace-branding.service.ts | Adds service to resolve workspace branding + cache index.html template |
| packages/twenty-server/src/engine/core-modules/workspace-branding/workspace-branding.module.ts | New module wiring for branding service/middleware/controller |
| packages/twenty-server/src/engine/core-modules/workspace-branding/workspace-branding.middleware.ts | Middleware serving branded SPA HTML + dynamic manifest + favicon redirects |
| packages/twenty-server/src/engine/core-modules/workspace-branding/workspace-branding.controller.ts | Controller for /favicon.ico (currently overlaps with middleware) |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/is-static-asset-path.util.ts | Utility to prevent branding HTML injection on API/static paths |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/inject-workspace-branding-into-index-html.util.ts | Injects workspace-specific meta/title/icon tags into an HTML template |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-request-origin.util.ts | Extracts origin for branding resolution (uses forwarded headers) |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-icon-mime-type-from-url.util.ts | MIME/sizes helpers for manifest icon selection |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-front-index-html-path.util.ts | Locates built front index.html |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-front-dist-path.util.ts | Locates built front dist directory across environments |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/generate-workspace-manifest.util.ts | Generates branded or neutral fallback PWA manifest |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/tests/inject-workspace-branding-into-index-html.util.spec.ts | Adds unit tests for HTML injection |
| packages/twenty-server/src/engine/core-modules/workspace-branding/utils/tests/generate-workspace-manifest.util.spec.ts | Adds unit tests for manifest generation |
| packages/twenty-server/src/engine/core-modules/core-engine.module.ts | Registers WorkspaceBrandingModule in core engine |
| packages/twenty-server/src/app.module.ts | Serves static front with index: false and applies branding middleware on GET * |
| packages/twenty-front/src/utils/title-utils.ts | Default title becomes “CRM” (i18n string) |
| packages/twenty-front/src/utils/tests/title-utils.test.ts | Updates tests for new default title |
| packages/twenty-front/src/pages/auth/SignInUp.tsx | Uses workspace logo as primary; changes “Welcome to Twenty” copy |
| packages/twenty-front/src/modules/ui/utilities/page-favicon/components/PageFavicon.tsx | Updates favicon/manifest behavior to align with server-side branding |
| packages/twenty-front/src/modules/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName.ts | Default workspace name no longer “Twenty” |
| packages/twenty-front/src/modules/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo.ts | Removes Twenty CDN logo fallback |
| packages/twenty-front/src/modules/ui/input/components/ImageInput.tsx | Forces <img> refresh when pictureURI changes |
| packages/twenty-front/src/modules/auth/sign-in-up/components/FooterNote.tsx | Removes Twenty legal links from sign-in footer; simplifies bypass |
| packages/twenty-front/src/modules/auth/components/Logo.tsx | Removes default Twenty icon fallback and adjusts rendering when no logo is set |
| packages/twenty-front/src/modules/activities/timeline-activities/utils/getTimelineActivityAuthorFullName.ts | System events read “System” instead of “Twenty” |
| packages/twenty-front/public/manifest.json | Replaces large icon set with neutral single icon |
| packages/twenty-front/public/branding/default-app-icon.svg | Adds neutral default app icon asset |
| packages/twenty-front/public/branding/README.md | Documents neutral branding assets and replacement steps |
| packages/twenty-front/index.html | Adds branding injection anchor block and neutral defaults |
| .github/workflows/tofu-upstream-sync.yaml | Weekly workflow to open upstream sync PRs |
| .github/workflows/tofu-docker-publish.yaml | Publishes GHCR image on tag push / manual dispatch |
| .github/workflows/cd-deploy-tag.yaml | Prevents upstream deploy workflow from running in fork |
| .github/workflows/cd-deploy-main.yaml | Prevents upstream deploy workflow from running in fork |
Comments suppressed due to low confidence (2)
packages/twenty-server/src/engine/core-modules/workspace-branding/workspace-branding.middleware.ts:1
- This middleware fully handles
/favicon.ico, but there is also aWorkspaceBrandingControllerthat definesGET favicon.icoand returns404when no branding is found. With the middleware applied to all GET routes, the controller path is effectively shadowed, and the two implementations disagree on the fallback behavior (302 to default asset vs 404). Pick a single owner for/favicon.ico(recommended: middleware only, remove the controller route) to avoid confusion and future regressions.
packages/twenty-server/src/engine/core-modules/workspace-branding/workspace-branding.service.ts:1 readFileSynccan throw (permissions, transient filesystem issues, partial image builds), which would fail module initialization and can prevent the server from starting. Wrap thereadFileSyncin a try/catch, log a warning, and leaveindexHtmlTemplateasnullso the middleware safely falls back tonext()behavior.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Create sync branch and merge upstream | ||
| id: merge | ||
| run: | | ||
| SYNC_BRANCH="upstream/sync-$(date -u +%Y-%m-%d)" | ||
| echo "branch=${SYNC_BRANCH}" >> "$GITHUB_OUTPUT" | ||
| git checkout -B "$SYNC_BRANCH" | ||
| if git merge upstream/main -m "chore: sync upstream Twenty ($(date -u +%Y-%m-%d))"; then | ||
| echo "status=clean" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "status=conflicts" >> "$GITHUB_OUTPUT" | ||
| fi |
| const forwardedProto = request.headers['x-forwarded-proto']; | ||
| const protocol = | ||
| typeof forwardedProto === 'string' | ||
| ? forwardedProto.split(',')[0]?.trim() | ||
| : request.protocol; | ||
|
|
||
| const forwardedHost = request.headers['x-forwarded-host']; | ||
| const hostHeader = | ||
| typeof forwardedHost === 'string' | ||
| ? forwardedHost.split(',')[0]?.trim() | ||
| : request.headers.host; | ||
|
|
||
| const hostname = hostHeader ?? request.hostname; | ||
|
|
||
| return `${protocol}://${hostname}`; |
| // No logo uploaded yet — link back to default domain so admins can | ||
| // still navigate, but show nothing instead of the Twenty icon. | ||
| <UndecoratedLink | ||
| to={AppPath.SignInUp} | ||
| onClick={redirectToDefaultDomain} | ||
| > | ||
| <StyledPrimaryLogo | ||
| style={{ backgroundImage: `url(${primaryLogoUrl})` }} | ||
| /> | ||
| </UndecoratedLink> | ||
| ) : ( | ||
| <StyledPrimaryLogo | ||
| style={{ backgroundImage: `url(${primaryLogoUrl})` }} | ||
| /> |
| const workspaceLogo = workspacePublicData?.logo ?? currentWorkspace?.logo; | ||
| const workspaceDisplayName = | ||
| workspacePublicData?.displayName ?? | ||
| currentWorkspace?.displayName ?? | ||
| DEFAULT_PWA_NAME; | ||
|
|
||
| const hasWorkspaceLogo = isNonEmptyString(workspaceLogo); | ||
| const faviconUrl = hasWorkspaceLogo | ||
| ? workspaceLogo | ||
| : getServerFaviconUrl(); |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 743c047165
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const STATIC_ASSET_PATH_PATTERN = | ||
| /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|map|json|txt|webp|wasm)$/i; | ||
|
|
||
| const API_PATH_PREFIXES = [ |
There was a problem hiding this comment.
Let API GET routes bypass the branding middleware
Because WorkspaceBrandingMiddleware is registered for every GET request in AppModule, any controller path missing from this allow-list is answered with the SPA index before the controller runs. I checked existing GET controllers such as @Controller('apps/oauth') for /apps/oauth/authorize and @Controller('.well-known') for /.well-known/oauth-authorization-server; neither prefix is listed here, so those endpoints now return branded HTML instead of the OAuth redirect/metadata JSON, breaking connection OAuth and OAuth discovery.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
23 issues found across 46 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tofu/BRANDING.md">
<violation number="1" location="tofu/BRANDING.md:74">
P2: The grep audit command is case-sensitive and will miss differently-cased branding strings.</violation>
</file>
<file name="packages/twenty-front/src/modules/ui/input/components/ImageInput.tsx">
<violation number="1" location="packages/twenty-front/src/modules/ui/input/components/ImageInput.tsx:138">
P1: `isPictureURLError` is never reset when `picture` changes, so a single `onError` permanently suppresses the `<img>` element even after a valid URL is provided. The new `key={pictureURI}` is ineffective because the image branch is gated by `!isPictureURLError`.</violation>
</file>
<file name="tofu/PRICING.md">
<violation number="1" location="tofu/PRICING.md:80">
P1: Launch tier margin calculation contradicts its own data-safety requirement: Tier 1's Supabase Free caveat says Pro is required before real client data lands, but Launch promises data migration while using Tier 1 ($17) instead of Tier 2 ($65) for its margin math.</violation>
<violation number="2" location="tofu/PRICING.md:117">
P2: Enterprise tier low-end gross margin percentage is incorrect: using the document's own numbers ($15,000 pricing minus $4,050 cost), the margin is ~73%, not 80%.</violation>
<violation number="3" location="tofu/PRICING.md:200">
P2: Arithmetic error in MRR example: 3 Custom Ops ($2,500) + 5 Growth Partner ($7,500) equals $45,000/mo, not $57,500/mo</violation>
</file>
<file name="packages/twenty-front/public/manifest.json">
<violation number="1" location="packages/twenty-front/public/manifest.json:10">
P1: Manifest replaced all platform-specific PNG icons with a single SVG icon, removing required PWA sizes (192x192, 512x512) and raster fallbacks that browsers rely on for installability.</violation>
</file>
<file name="tofu/FORK-MANAGEMENT.md">
<violation number="1" location="tofu/FORK-MANAGEMENT.md:215">
P2: Stale hard-coded AGPL cutoff in emergency freeze documentation creates legal risk</violation>
</file>
<file name="tofu/SETUP.md">
<violation number="1" location="tofu/SETUP.md:99">
P2: `sed -i ''` uses BSD/macOS syntax that fails on GNU/Linux (typical VPS OS). For cross-platform compatibility, replace the BSD-specific in-place sed commands with a portable alternative.</violation>
</file>
<file name="packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-request-origin.util.ts">
<violation number="1" location="packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-request-origin.util.ts:3">
P2: `getRequestOrigin` reads raw `X-Forwarded-*` headers directly instead of using Express's `request.protocol` and `request.hostname`, bypassing the configured `trust proxy` boundary.</violation>
<violation number="2" location="packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-request-origin.util.ts:16">
P2: Empty forwarded header tokens are kept as valid values instead of falling back to safe defaults. `split(',')[0]?.trim()` can produce an empty string for malformed headers like `x-forwarded-host: ,`, and `??` does not treat empty strings as absent, so the fallback to `request.hostname` / `request.protocol` never triggers.</violation>
</file>
<file name="tofu/DEPLOYMENT-DIGITALOCEAN.md">
<violation number="1" location="tofu/DEPLOYMENT-DIGITALOCEAN.md:45">
P2: Invalid `doctl` command documented for one-off exec: `doctl apps tier instance exec ...` does not exist. `doctl apps tier` manages App Platform pricing tiers, not instance execution. App Platform does not provide a non-interactive `exec` command; operators should use `doctl apps console` (already documented above) for shell access.</violation>
</file>
<file name="packages/twenty-server/src/engine/core-modules/workspace-branding/workspace-branding.service.ts">
<violation number="1" location="packages/twenty-server/src/engine/core-modules/workspace-branding/workspace-branding.service.ts:47">
P2: Uncaught `readFileSync` error in `onModuleInit` can crash startup instead of gracefully disabling branding</violation>
</file>
<file name="tofu/CLIENT-CUSTOMIZATION.md">
<violation number="1" location="tofu/CLIENT-CUSTOMIZATION.md:26">
P2: Conflicting documentation about no-logo fallback behavior on sign-in page</violation>
</file>
<file name="tofu/DATABASE.md">
<violation number="1" location="tofu/DATABASE.md:87">
P1: Supabase setup instructions are inconsistent: the SQL creates a `twenty_production` database, but the connection strings later connect to the built-in `postgres` database. Supabase projects have a single managed database per project; schema-level isolation should be used instead.</violation>
</file>
<file name="tofu/README.md">
<violation number="1" location="tofu/README.md:55">
P2: README Quick Start includes non-portable, machine-specific absolute filesystem paths that only exist on one developer's machine.</violation>
</file>
<file name=".github/workflows/tofu-upstream-sync.yaml">
<violation number="1" location=".github/workflows/tofu-upstream-sync.yaml:36">
P2: Workflow may fail clean merges because no git committer identity is configured before `git merge` creates a commit.</violation>
<violation number="2" location=".github/workflows/tofu-upstream-sync.yaml:36">
P1: Merge failures are not fail-closed: when `git merge upstream/main` fails with conflicts, no merge commit is created and HEAD remains at the original commit, yet the workflow continues to force-push and attempt to open a PR. This results in `gh pr create` failing or creating an empty PR with zero commits to merge.</violation>
<violation number="3" location=".github/workflows/tofu-upstream-sync.yaml:47">
P2: Unconditional `--force` push before PR-existence check can clobber manual updates on same-day sync branches</violation>
</file>
<file name=".github/workflows/tofu-docker-publish.yaml">
<violation number="1" location=".github/workflows/tofu-docker-publish.yaml:38">
P2: Workflow dispatch input `tag` is emitted directly to `$GITHUB_OUTPUT` without validation. Malformed input (newlines, invalid Docker tag characters) can break output parsing and produce invalid Docker tags downstream.</violation>
</file>
<file name="packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-icon-mime-type-from-url.util.ts">
<violation number="1" location="packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-icon-mime-type-from-url.util.ts:1">
P1: `getIconMimeTypeFromUrl` checks the full URL string with `endsWith('.svg')` / `endsWith('.ico')`, which breaks for signed CDN URLs that include query parameters or hashes. The `logoUrl` comes from `signWorkspaceLogoUrl`, a service that typically appends auth tokens as query params. An SVG logo with params will be mis-detected as `image/png`, the manifest will emit raster sizes instead of `any`, and browsers may ignore or mis-handle the icon entry.</violation>
<violation number="2" location="packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-icon-mime-type-from-url.util.ts:12">
P2: Hard-coded PNG fallback misclassifies non-PNG icon URLs in manifest metadata.</violation>
</file>
<file name="packages/twenty-server/src/engine/core-modules/workspace-branding/utils/is-static-asset-path.util.ts">
<violation number="1" location="packages/twenty-server/src/engine/core-modules/workspace-branding/utils/is-static-asset-path.util.ts:4">
P0: The `API_PATH_PREFIXES` allow-list is incomplete — it omits GET controller routes like `/apps/oauth/authorize` and `/.well-known/oauth-authorization-server`. Since `WorkspaceBrandingMiddleware` is registered for `path: '*', method: GET` and NestJS middleware runs before controllers, any route not in this list will receive branded HTML instead of the controller's response. This breaks OAuth connection flows and OIDC discovery. Add missing prefixes (at minimum `/apps/`, `/.well-known/`) or invert the approach by explicitly listing SPA-eligible paths.</violation>
</file>
<file name="packages/twenty-front/src/modules/ui/utilities/page-favicon/components/PageFavicon.tsx">
<violation number="1" location="packages/twenty-front/src/modules/ui/utilities/page-favicon/components/PageFavicon.tsx:83">
P2: `faviconUrl` uses the raw `workspaceLogo` value without normalizing to an absolute URL. If the logo is stored as a relative path (e.g., `/files/workspace-logo.png`), the `<link rel="icon">` and manifest icon entries may not resolve correctly in all contexts (e.g., PWA manifest requires absolute URLs for icons). Apply `getImageAbsoluteURI` with `REACT_APP_SERVER_BASE_URL` before using the value, consistent with how other components handle workspace logos.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
| const STATIC_ASSET_PATH_PATTERN = | ||
| /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|map|json|txt|webp|wasm)$/i; | ||
|
|
||
| const API_PATH_PREFIXES = [ |
There was a problem hiding this comment.
P0: The API_PATH_PREFIXES allow-list is incomplete — it omits GET controller routes like /apps/oauth/authorize and /.well-known/oauth-authorization-server. Since WorkspaceBrandingMiddleware is registered for path: '*', method: GET and NestJS middleware runs before controllers, any route not in this list will receive branded HTML instead of the controller's response. This breaks OAuth connection flows and OIDC discovery. Add missing prefixes (at minimum /apps/, /.well-known/) or invert the approach by explicitly listing SPA-eligible paths.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-server/src/engine/core-modules/workspace-branding/utils/is-static-asset-path.util.ts, line 4:
<comment>The `API_PATH_PREFIXES` allow-list is incomplete — it omits GET controller routes like `/apps/oauth/authorize` and `/.well-known/oauth-authorization-server`. Since `WorkspaceBrandingMiddleware` is registered for `path: '*', method: GET` and NestJS middleware runs before controllers, any route not in this list will receive branded HTML instead of the controller's response. This breaks OAuth connection flows and OIDC discovery. Add missing prefixes (at minimum `/apps/`, `/.well-known/`) or invert the approach by explicitly listing SPA-eligible paths.</comment>
<file context>
@@ -0,0 +1,28 @@
+const STATIC_ASSET_PATH_PATTERN =
+ /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|map|json|txt|webp|wasm)$/i;
+
+const API_PATH_PREFIXES = [
+ '/graphql',
+ '/metadata',
</file context>
| > | ||
| {pictureURI && !isPictureURLError ? ( | ||
| <img | ||
| key={pictureURI} |
There was a problem hiding this comment.
P1: isPictureURLError is never reset when picture changes, so a single onError permanently suppresses the <img> element even after a valid URL is provided. The new key={pictureURI} is ineffective because the image branch is gated by !isPictureURLError.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/ui/input/components/ImageInput.tsx, line 138:
<comment>`isPictureURLError` is never reset when `picture` changes, so a single `onError` permanently suppresses the `<img>` element even after a valid URL is provided. The new `key={pictureURI}` is ineffective because the image branch is gated by `!isPictureURLError`.</comment>
<file context>
@@ -135,6 +135,7 @@ export const ImageInput = ({
>
{pictureURI && !isPictureURLError ? (
<img
+ key={pictureURI}
src={pictureURI}
alt="profile"
</file context>
| - 30 days of post-launch support included | ||
| - Then $500/mo for hosting + uptime monitoring + Twenty upstream updates | ||
|
|
||
| **Our margin:** $500 − $17 (Tier 1 infra) = **$483/mo margin (97%)** |
There was a problem hiding this comment.
P1: Launch tier margin calculation contradicts its own data-safety requirement: Tier 1's Supabase Free caveat says Pro is required before real client data lands, but Launch promises data migration while using Tier 1 ($17) instead of Tier 2 ($65) for its margin math.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tofu/PRICING.md, line 80:
<comment>Launch tier margin calculation contradicts its own data-safety requirement: Tier 1's Supabase Free caveat says Pro is required before real client data lands, but Launch promises data migration while using Tier 1 ($17) instead of Tier 2 ($65) for its margin math.</comment>
<file context>
@@ -0,0 +1,224 @@
+- 30 days of post-launch support included
+- Then $500/mo for hosting + uptime monitoring + Twenty upstream updates
+
+**Our margin:** $500 − $17 (Tier 1 infra) = **$483/mo margin (97%)**
+
+#### **Custom Ops** — one-time $25,000–$50,000 + $2,500/mo managed
</file context>
| @@ -1,458 +1,16 @@ | |||
| { | |||
There was a problem hiding this comment.
P1: Manifest replaced all platform-specific PNG icons with a single SVG icon, removing required PWA sizes (192x192, 512x512) and raster fallbacks that browsers rely on for installability.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/public/manifest.json, line 10:
<comment>Manifest replaced all platform-specific PNG icons with a single SVG icon, removing required PWA sizes (192x192, 512x512) and raster fallbacks that browsers rely on for installability.</comment>
<file context>
@@ -1,458 +1,16 @@
- {
- "src": "images/icons/ios/1024.png",
- "sizes": "1024x1024"
+ "src": "/branding/default-app-icon.svg",
+ "sizes": "any",
+ "type": "image/svg+xml",
</file context>
| -- Create a dedicated user + database for Twenty | ||
| -- Safer than using the default postgres role | ||
| CREATE USER twenty_app WITH PASSWORD 'CHANGE_ME_HEX_PASSWORD'; | ||
| CREATE DATABASE twenty_production OWNER twenty_app; |
There was a problem hiding this comment.
P1: Supabase setup instructions are inconsistent: the SQL creates a twenty_production database, but the connection strings later connect to the built-in postgres database. Supabase projects have a single managed database per project; schema-level isolation should be used instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tofu/DATABASE.md, line 87:
<comment>Supabase setup instructions are inconsistent: the SQL creates a `twenty_production` database, but the connection strings later connect to the built-in `postgres` database. Supabase projects have a single managed database per project; schema-level isolation should be used instead.</comment>
<file context>
@@ -0,0 +1,218 @@
+-- Create a dedicated user + database for Twenty
+-- Safer than using the default postgres role
+CREATE USER twenty_app WITH PASSWORD 'CHANGE_ME_HEX_PASSWORD';
+CREATE DATABASE twenty_production OWNER twenty_app;
+GRANT ALL PRIVILEGES ON DATABASE twenty_production TO twenty_app;
+```
</file context>
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| SYNC_BRANCH: ${{ steps.merge.outputs.branch }} | ||
| run: | | ||
| git push -u origin "$SYNC_BRANCH" --force |
There was a problem hiding this comment.
P2: Unconditional --force push before PR-existence check can clobber manual updates on same-day sync branches
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/tofu-upstream-sync.yaml, line 47:
<comment>Unconditional `--force` push before PR-existence check can clobber manual updates on same-day sync branches</comment>
<file context>
@@ -0,0 +1,71 @@
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SYNC_BRANCH: ${{ steps.merge.outputs.branch }}
+ run: |
+ git push -u origin "$SYNC_BRANCH" --force
+
+ - name: Open pull request
</file context>
| SYNC_BRANCH="upstream/sync-$(date -u +%Y-%m-%d)" | ||
| echo "branch=${SYNC_BRANCH}" >> "$GITHUB_OUTPUT" | ||
| git checkout -B "$SYNC_BRANCH" | ||
| if git merge upstream/main -m "chore: sync upstream Twenty ($(date -u +%Y-%m-%d))"; then |
There was a problem hiding this comment.
P2: Workflow may fail clean merges because no git committer identity is configured before git merge creates a commit.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/tofu-upstream-sync.yaml, line 36:
<comment>Workflow may fail clean merges because no git committer identity is configured before `git merge` creates a commit.</comment>
<file context>
@@ -0,0 +1,71 @@
+ SYNC_BRANCH="upstream/sync-$(date -u +%Y-%m-%d)"
+ echo "branch=${SYNC_BRANCH}" >> "$GITHUB_OUTPUT"
+ git checkout -B "$SYNC_BRANCH"
+ if git merge upstream/main -m "chore: sync upstream Twenty ($(date -u +%Y-%m-%d))"; then
+ echo "status=clean" >> "$GITHUB_OUTPUT"
+ else
</file context>
| else | ||
| TAG="${GITHUB_REF_NAME}" | ||
| fi | ||
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" |
There was a problem hiding this comment.
P2: Workflow dispatch input tag is emitted directly to $GITHUB_OUTPUT without validation. Malformed input (newlines, invalid Docker tag characters) can break output parsing and produce invalid Docker tags downstream.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/tofu-docker-publish.yaml, line 38:
<comment>Workflow dispatch input `tag` is emitted directly to `$GITHUB_OUTPUT` without validation. Malformed input (newlines, invalid Docker tag characters) can break output parsing and produce invalid Docker tags downstream.</comment>
<file context>
@@ -0,0 +1,63 @@
+ else
+ TAG="${GITHUB_REF_NAME}"
+ fi
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+
+ - name: Set up Docker Buildx
</file context>
| return 'image/x-icon'; | ||
| } | ||
|
|
||
| return 'image/png'; |
There was a problem hiding this comment.
P2: Hard-coded PNG fallback misclassifies non-PNG icon URLs in manifest metadata.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-server/src/engine/core-modules/workspace-branding/utils/get-icon-mime-type-from-url.util.ts, line 12:
<comment>Hard-coded PNG fallback misclassifies non-PNG icon URLs in manifest metadata.</comment>
<file context>
@@ -0,0 +1,20 @@
+ return 'image/x-icon';
+ }
+
+ return 'image/png';
+};
+
</file context>
|
|
||
| const hasWorkspaceLogo = isNonEmptyString(workspaceLogo); | ||
| const faviconUrl = hasWorkspaceLogo | ||
| ? workspaceLogo |
There was a problem hiding this comment.
P2: faviconUrl uses the raw workspaceLogo value without normalizing to an absolute URL. If the logo is stored as a relative path (e.g., /files/workspace-logo.png), the <link rel="icon"> and manifest icon entries may not resolve correctly in all contexts (e.g., PWA manifest requires absolute URLs for icons). Apply getImageAbsoluteURI with REACT_APP_SERVER_BASE_URL before using the value, consistent with how other components handle workspace logos.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/ui/utilities/page-favicon/components/PageFavicon.tsx, line 83:
<comment>`faviconUrl` uses the raw `workspaceLogo` value without normalizing to an absolute URL. If the logo is stored as a relative path (e.g., `/files/workspace-logo.png`), the `<link rel="icon">` and manifest icon entries may not resolve correctly in all contexts (e.g., PWA manifest requires absolute URLs for icons). Apply `getImageAbsoluteURI` with `REACT_APP_SERVER_BASE_URL` before using the value, consistent with how other components handle workspace logos.</comment>
<file context>
@@ -1,26 +1,131 @@
+
+ const hasWorkspaceLogo = isNonEmptyString(workspaceLogo);
+ const faviconUrl = hasWorkspaceLogo
+ ? workspaceLogo
+ : getServerFaviconUrl();
+
</file context>
Summary
Domain / URL link chips in table views clipped label text vertically (letters cut off at top and bottom), making links look broken or struck through.
Root cause:
RoundedLinksetheight: 10pxon the content box withoverflow: hidden, while labels use ~12px font — text overflow was hidden.Fix: Align with
Chipsmall sizing:box-sizing: border-box,height: spacing[3],font-size: sm, flex centering, and correctfont-weight(was incorrectly using thefont.size.mdtoken).Test plan
RoundedLinkstill look correctFixes visual regression in dense record tables (Companies domain column).
Made with Cursor