diff --git a/.opencode/skills/envelope-editor-v2-e2e/SKILL.md b/.opencode/skills/envelope-editor-v2-e2e/SKILL.md
new file mode 100644
index 000000000..0ac584a76
--- /dev/null
+++ b/.opencode/skills/envelope-editor-v2-e2e/SKILL.md
@@ -0,0 +1,371 @@
+---
+name: envelope-editor-v2-e2e
+description: Writing and maintaining Playwright E2E tests for the Envelope Editor V2. Use when the user needs to create, modify, debug, or extend E2E tests in packages/app-tests/e2e/envelope-editor-v2/. Triggers include requests to "write an e2e test", "add a test for the envelope editor", "test envelope settings/recipients/fields/items/attachments", "fix a failing envelope test", or any task involving Playwright tests for the envelope editor feature.
+---
+
+# Envelope Editor V2 E2E Tests
+
+## Overview
+
+The Envelope Editor V2 E2E test suite lives in `packages/app-tests/e2e/envelope-editor-v2/`. Each test file covers a distinct feature area of the envelope editor and follows a strict architectural pattern that tests the **same flow** across four surfaces:
+
+1. **Document** (`documents/`) - Native document editor
+2. **Template** (`templates/`) - Native template editor
+3. **Embedded Create** (`/embed/v2/authoring/envelope/create`) - Embedded editor creating a new envelope
+4. **Embedded Edit** (`/embed/v2/authoring/envelope/edit/`) - Embedded editor updating an existing envelope
+
+## Project Structure
+
+```
+packages/app-tests/
+ e2e/
+ envelope-editor-v2/
+ envelope-attachments.spec.ts # Attachment CRUD
+ envelope-fields.spec.ts # Field placement on PDF canvas
+ envelope-items.spec.ts # PDF document item CRUD
+ envelope-recipients.spec.ts # Recipient management
+ envelope-settings.spec.ts # Settings dialog
+ fixtures/
+ authentication.ts # apiSignin, apiSignout
+ documents.ts # Document tab helpers
+ envelope-editor.ts # Core fixture: surface openers + locator/action helpers
+ generic.ts # Toast assertions, text visibility
+ signature.ts # Signature pad helpers
+ playwright.config.ts # Test configuration
+```
+
+## Core Abstraction: `TEnvelopeEditorSurface`
+
+Every test revolves around the `TEnvelopeEditorSurface` type from `fixtures/envelope-editor.ts`. This is the central abstraction that normalizes differences between the four surfaces:
+
+```typescript
+type TEnvelopeEditorSurface = {
+ root: Page; // The Playwright page
+ isEmbedded: boolean; // true for embed surfaces
+ envelopeId?: string; // Set for document/template/embed-edit, undefined for embed-create
+ envelopeType: 'DOCUMENT' | 'TEMPLATE';
+ userId: number; // Seeded user ID
+ userEmail: string; // Seeded user email
+ userName: string; // Seeded user name
+ teamId: number; // Seeded team ID
+};
+```
+
+### Surface Openers (from `fixtures/envelope-editor.ts`)
+
+```typescript
+// Native surfaces - seed user + document/template, sign in, navigate
+const surface = await openDocumentEnvelopeEditor(page);
+const surface = await openTemplateEnvelopeEditor(page);
+
+// Embedded surfaces - seed user, create API token, get presign token, navigate
+const surface = await openEmbeddedEnvelopeEditor(page, {
+ envelopeType: 'DOCUMENT' | 'TEMPLATE',
+ mode?: 'create' | 'edit', // default: 'create'
+ tokenNamePrefix?: string, // for unique API token names
+ externalId?: string, // optional external ID in hash
+ features?: EmbeddedEditorConfig, // feature flags
+});
+```
+
+## Test Architecture Pattern
+
+Every test file follows this structure, with four `test.describe` blocks grouping tests by editor surface:
+
+### 1. Imports
+
+```typescript
+import { type Page, expect, test } from '@playwright/test';
+// Prisma enums if needed for DB assertions
+import { SomePrismaEnum } from '@prisma/client';
+
+import { nanoid } from '@documenso/lib/universal/id';
+import { prisma } from '@documenso/prisma';
+
+import {
+ type TEnvelopeEditorSurface, // Import needed helpers from the fixture
+ openDocumentEnvelopeEditor,
+ openEmbeddedEnvelopeEditor,
+ openTemplateEnvelopeEditor,
+ persistEmbeddedEnvelope, // ... other helpers
+} from '../fixtures/envelope-editor';
+import { expectToastTextToBeVisible } from '../fixtures/generic';
+```
+
+### 2. Type definitions and constants
+
+```typescript
+type FlowResult = {
+ externalId: string;
+ // ... other data needed for DB assertions
+};
+
+const TEST_VALUES = {
+ // Centralized test data constants
+};
+```
+
+### 3. Local helper functions
+
+```typescript
+// Common: open settings and set external ID for DB lookup
+const openSettingsDialog = async (root: Page) => {
+ await getEnvelopeEditorSettingsTrigger(root).click();
+ await expect(root.getByRole('heading', { name: 'Document Settings' })).toBeVisible();
+};
+
+const updateExternalId = async (surface: TEnvelopeEditorSurface, externalId: string) => {
+ await openSettingsDialog(surface.root);
+ await surface.root.locator('input[name="externalId"]').fill(externalId);
+ await surface.root.getByRole('button', { name: 'Update' }).click();
+
+ if (!surface.isEmbedded) {
+ await expectToastTextToBeVisible(surface.root, 'Envelope updated');
+ }
+};
+```
+
+### 4. The flow function
+
+A single `runXxxFlow` function that works across ALL surfaces. It handles embedded vs non-embedded differences internally:
+
+```typescript
+const runMyFeatureFlow = async (surface: TEnvelopeEditorSurface): Promise => {
+ const externalId = `e2e-feature-${nanoid()}`;
+
+ // For embedded create, may need to add a PDF first
+ if (surface.isEmbedded && !surface.envelopeId) {
+ await addEnvelopeItemPdf(surface.root, 'embedded-feature.pdf');
+ }
+
+ await updateExternalId(surface, externalId);
+
+ // Handle embedded vs native differences
+ if (surface.isEmbedded) {
+ // No "Add Myself" button in embedded mode
+ await setRecipientEmail(surface.root, 0, 'embedded@example.com');
+ } else {
+ await clickAddMyselfButton(surface.root);
+ }
+
+ // ... perform feature-specific actions ...
+
+ // Navigate away and back to verify UI persistence
+ await clickEnvelopeEditorStep(surface.root, 'addFields');
+ await clickEnvelopeEditorStep(surface.root, 'upload');
+
+ // ... assert UI state after navigation ...
+
+ return { externalId /* ... */ };
+};
+```
+
+### 5. Database assertion function
+
+Uses Prisma directly to verify data was persisted correctly:
+
+```typescript
+const assertFeaturePersistedInDatabase = async ({
+ surface,
+ externalId,
+ // ... expected values
+}: {
+ surface: TEnvelopeEditorSurface;
+ externalId: string;
+ // ...
+}) => {
+ const envelope = await prisma.envelope.findFirstOrThrow({
+ where: {
+ externalId,
+ userId: surface.userId,
+ teamId: surface.teamId,
+ type: surface.envelopeType,
+ },
+ include: {
+ // Include related data as needed
+ documentMeta: true,
+ recipients: true,
+ fields: true,
+ envelopeAttachments: true,
+ },
+ orderBy: { createdAt: 'desc' },
+ });
+
+ // Assert expected values
+ expect(envelope.someField).toBe(expectedValue);
+};
+```
+
+### 6. The four `test.describe` blocks
+
+Tests are organized into four `test.describe` blocks, one per editor surface. Each describe block contains the tests relevant to that surface. This structure allows adding multiple tests per surface while keeping them grouped:
+
+```typescript
+test.describe('document editor', () => {
+ test('description of what is tested', async ({ page }) => {
+ const surface = await openDocumentEnvelopeEditor(page);
+ const result = await runMyFeatureFlow(surface);
+
+ await assertFeaturePersistedInDatabase({
+ surface,
+ ...result,
+ });
+ });
+
+ // Additional document-editor-specific tests here...
+});
+
+test.describe('template editor', () => {
+ test('description of what is tested', async ({ page }) => {
+ const surface = await openTemplateEnvelopeEditor(page);
+ const result = await runMyFeatureFlow(surface);
+
+ await assertFeaturePersistedInDatabase({
+ surface,
+ ...result,
+ });
+ });
+
+ // Additional template-editor-specific tests here...
+});
+
+test.describe('embedded create', () => {
+ test('description of what is tested', async ({ page }) => {
+ const surface = await openEmbeddedEnvelopeEditor(page, {
+ envelopeType: 'DOCUMENT',
+ tokenNamePrefix: 'e2e-embed-feature',
+ });
+
+ const result = await runMyFeatureFlow(surface);
+
+ // IMPORTANT: Must persist before DB assertions for embedded
+ await persistEmbeddedEnvelope(surface);
+
+ await assertFeaturePersistedInDatabase({
+ surface,
+ ...result,
+ });
+ });
+
+ // Additional embedded-create-specific tests here...
+});
+
+test.describe('embedded edit', () => {
+ test('description of what is tested', async ({ page }) => {
+ const surface = await openEmbeddedEnvelopeEditor(page, {
+ envelopeType: 'TEMPLATE',
+ mode: 'edit',
+ tokenNamePrefix: 'e2e-embed-feature',
+ });
+
+ const result = await runMyFeatureFlow(surface);
+
+ // IMPORTANT: Must persist before DB assertions for embedded
+ await persistEmbeddedEnvelope(surface);
+
+ await assertFeaturePersistedInDatabase({
+ surface,
+ ...result,
+ });
+ });
+
+ // Additional embedded-edit-specific tests here...
+});
+```
+
+When a test only applies to specific surfaces (e.g., a document-only action like "send document"), only include it in the relevant describe block(s). Not every describe block needs the same tests -- the structure groups tests by surface, not by requiring symmetry.
+
+## Key Differences Between Surfaces
+
+| Behavior | Document/Template | Embedded Create | Embedded Edit |
+| -------------------------- | -------------------------- | ----------------------------------------- | ----------------------------------------- |
+| User seeding | Seed + sign in | Seed + API token | Seed + API token + seed envelope |
+| "Add Myself" button | Available | Not available | Not available |
+| Toast on settings update | Yes (`'Envelope updated'`) | No | No |
+| PDF already attached | Yes (1 item) | No (0 items, must upload) | Yes (1 item) |
+| Delete confirmation dialog | Yes (`'Delete'` button) | No (immediate) | No (immediate) |
+| DB persistence timing | Immediate (autosaved) | After `persistEmbeddedEnvelope()` | After `persistEmbeddedEnvelope()` |
+| Persist button label | N/A | `'Create Document'` / `'Create Template'` | `'Update Document'` / `'Update Template'` |
+
+## Available Fixture Helpers
+
+### From `fixtures/envelope-editor.ts`
+
+**Locator helpers** (return Playwright Locators):
+
+- `getEnvelopeEditorSettingsTrigger(root)` - Settings gear button
+- `getEnvelopeItemTitleInputs(root)` - Title inputs for envelope items
+- `getEnvelopeItemDragHandles(root)` - Drag handles for reordering items
+- `getEnvelopeItemRemoveButtons(root)` - Remove buttons for items
+- `getEnvelopeItemDropzoneInput(root)` - File input for PDF upload
+- `getRecipientEmailInputs(root)` - Email inputs for recipients
+- `getRecipientNameInputs(root)` - Name inputs for recipients
+- `getRecipientRows(root)` - Full recipient row fieldsets
+- `getRecipientRemoveButtons(root)` - Remove buttons for recipients
+- `getSigningOrderInputs(root)` - Signing order number inputs
+
+**Action helpers**:
+
+- `addEnvelopeItemPdf(root, fileName?)` - Upload a PDF to the dropzone
+- `clickEnvelopeEditorStep(root, stepId)` - Navigate to a step: `'upload'`, `'addFields'`, `'preview'`
+- `clickAddMyselfButton(root)` - Click "Add Myself" (native only)
+- `clickAddSignerButton(root)` - Click "Add Signer"
+- `setRecipientEmail(root, index, email)` - Fill recipient email
+- `setRecipientName(root, index, name)` - Fill recipient name
+- `setRecipientRole(root, index, roleLabel)` - Set role via combobox
+- `assertRecipientRole(root, index, roleLabel)` - Assert role value
+- `toggleSigningOrder(root, enabled)` - Toggle signing order switch
+- `toggleAllowDictateSigners(root, enabled)` - Toggle dictate signers switch
+- `setSigningOrderValue(root, index, value)` - Set signing order number
+- `persistEmbeddedEnvelope(surface)` - Click Create/Update button for embedded flows
+
+### From `fixtures/generic.ts`
+
+- `expectTextToBeVisible(page, text)` - Assert text visible on page
+- `expectTextToNotBeVisible(page, text)` - Assert text not visible
+- `expectToastTextToBeVisible(page, text)` - Assert toast message visible
+
+## External ID Pattern
+
+Every test uses an `externalId` (e.g., `e2e-feature-${nanoid()}`) set via the settings dialog. This unique ID is then used in Prisma queries to reliably locate the envelope in the database for assertions. This is critical because multiple tests run in parallel.
+
+## Running Tests
+
+```bash
+# Run all envelope editor tests
+npm run test:dev -w @documenso/app-tests -- --grep "Envelope Editor V2"
+
+# Run a specific test file
+npm run test:dev -w @documenso/app-tests -- e2e/envelope-editor-v2/envelope-recipients.spec.ts
+
+# Run with UI
+npm run test-ui:dev -w @documenso/app-tests -- e2e/envelope-editor-v2/
+
+# Run specific test by name
+npm run test:dev -w @documenso/app-tests -- --grep "documents/: add myself"
+```
+
+## Checklist When Writing a New Test
+
+1. Create the spec file in `packages/app-tests/e2e/envelope-editor-v2/`
+2. Import `TEnvelopeEditorSurface` and the three opener functions
+3. Import `persistEmbeddedEnvelope` if you need DB assertions for embedded flows
+4. Define a `FlowResult` type for data passed between flow and assertion
+5. Define `TEST_VALUES` constants for test data
+6. Write `updateExternalId` helper (or reuse the pattern)
+7. Write the `runXxxFlow` function handling embedded vs native differences
+8. Write the `assertXxxPersistedInDatabase` function using Prisma
+9. Create four `test.describe` blocks: `'document editor'`, `'template editor'`, `'embedded create'`, `'embedded edit'`
+10. Place tests inside the appropriate describe block for each surface
+11. For embedded create tests, add a PDF via `addEnvelopeItemPdf` before the flow
+12. For embedded tests, call `persistEmbeddedEnvelope(surface)` before DB assertions
+13. Use `surface.isEmbedded` to branch on behavioral differences (toasts, "Add Myself", etc.)
+
+## Common Pitfalls
+
+- **Missing `persistEmbeddedEnvelope`**: Embedded flows don't autosave. You MUST call this before any DB assertions.
+- **PDF required for embedded create**: Embedded create starts with 0 items. Upload a PDF before navigating to fields.
+- **Toast assertions in embedded**: Don't assert toasts for settings updates in embedded mode (they don't appear).
+- **Parallel test isolation**: Always use a unique `externalId` via `nanoid()` so parallel tests don't collide.
+- **Navigation verification**: Navigate away from and back to the current step to verify UI state persistence (the editor may re-render).
+- **Delete confirmation**: Native surfaces show a confirmation dialog for item deletion; embedded surfaces delete immediately.
diff --git a/apps/remix/app/components/dialogs/envelope-delete-dialog.tsx b/apps/remix/app/components/dialogs/envelope-delete-dialog.tsx
new file mode 100644
index 000000000..2975643f6
--- /dev/null
+++ b/apps/remix/app/components/dialogs/envelope-delete-dialog.tsx
@@ -0,0 +1,215 @@
+import { useEffect, useState } from 'react';
+
+import { msg } from '@lingui/core/macro';
+import { useLingui } from '@lingui/react/macro';
+import { Trans } from '@lingui/react/macro';
+import { DocumentStatus, EnvelopeType } from '@prisma/client';
+import { P, match } from 'ts-pattern';
+
+import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
+import { trpc as trpcReact } from '@documenso/trpc/react';
+import { Alert, AlertDescription } from '@documenso/ui/primitives/alert';
+import { Button } from '@documenso/ui/primitives/button';
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from '@documenso/ui/primitives/dialog';
+import { Input } from '@documenso/ui/primitives/input';
+import { useToast } from '@documenso/ui/primitives/use-toast';
+
+type EnvelopeDeleteDialogProps = {
+ id: string;
+ type: EnvelopeType;
+ trigger?: React.ReactNode;
+ onDelete?: () => Promise | void;
+ status: DocumentStatus;
+ title: string;
+ canManageDocument: boolean;
+};
+
+export const EnvelopeDeleteDialog = ({
+ id,
+ type,
+ trigger,
+ onDelete,
+ status,
+ title,
+ canManageDocument,
+}: EnvelopeDeleteDialogProps) => {
+ const { toast } = useToast();
+ const { refreshLimits } = useLimits();
+ const { t } = useLingui();
+
+ const deleteMessage = msg`delete`;
+
+ const [open, setOpen] = useState(false);
+ const [inputValue, setInputValue] = useState('');
+ const [isDeleteEnabled, setIsDeleteEnabled] = useState(status === DocumentStatus.DRAFT);
+
+ const { mutateAsync: deleteEnvelope, isPending } = trpcReact.envelope.delete.useMutation({
+ onSuccess: async () => {
+ void refreshLimits();
+
+ toast({
+ title: t`Document deleted`,
+ description: t`"${title}" has been successfully deleted`,
+ duration: 5000,
+ });
+
+ await onDelete?.();
+
+ setOpen(false);
+ },
+ onError: () => {
+ toast({
+ title: t`Something went wrong`,
+ description: t`This document could not be deleted at this time. Please try again.`,
+ variant: 'destructive',
+ duration: 7500,
+ });
+ },
+ });
+
+ useEffect(() => {
+ if (open) {
+ setInputValue('');
+ setIsDeleteEnabled(status === DocumentStatus.DRAFT);
+ }
+ }, [open, status]);
+
+ const onInputChange = (event: React.ChangeEvent) => {
+ setInputValue(event.target.value);
+ setIsDeleteEnabled(event.target.value === t(deleteMessage));
+ };
+
+ return (
+
+ );
+};
diff --git a/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx b/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
index a1943eb07..93e59ec67 100644
--- a/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
+++ b/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
@@ -13,6 +13,7 @@ import * as z from 'zod';
import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
+import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
import { getRecipientsWithMissingFields } from '@documenso/lib/utils/recipients';
import { trpc, trpc as trpcReact } from '@documenso/trpc/react';
@@ -116,10 +117,15 @@ export const EnvelopeDistributeDialog = ({
} = form;
const { data: emailData, isLoading: isLoadingEmails } =
- trpc.enterprise.organisation.email.find.useQuery({
- organisationId: organisation.id,
- perPage: 100,
- });
+ trpc.enterprise.organisation.email.find.useQuery(
+ {
+ organisationId: organisation.id,
+ perPage: 100,
+ },
+ {
+ ...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
+ },
+ );
const emails = emailData?.data || [];
diff --git a/apps/remix/app/components/embed/authoring/configure-fields-view.tsx b/apps/remix/app/components/embed/authoring/configure-fields-view.tsx
index 1a4bee33c..f651cd981 100644
--- a/apps/remix/app/components/embed/authoring/configure-fields-view.tsx
+++ b/apps/remix/app/components/embed/authoring/configure-fields-view.tsx
@@ -15,7 +15,7 @@ import { PDF_VIEWER_PAGE_SELECTOR, getPdfPagesCount } from '@documenso/lib/const
import { type TFieldMetaSchema, ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { nanoid } from '@documenso/lib/universal/id';
import { ADVANCED_FIELD_TYPES_WITH_OPTIONAL_SETTING } from '@documenso/lib/utils/advanced-fields-helpers';
-import { getDocumentDataUrl } from '@documenso/lib/utils/envelope-download';
+import { getDocumentDataUrlForPdfViewer } from '@documenso/lib/utils/envelope-download';
import { useRecipientColors } from '@documenso/ui/lib/recipient-colors';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
@@ -87,7 +87,7 @@ export const ConfigureFieldsView = ({
const normalizedDocumentData = useMemo(() => {
if (envelopeItem) {
- return getDocumentDataUrl({
+ return getDocumentDataUrlForPdfViewer({
envelopeId: envelopeItem.envelopeId,
envelopeItemId: envelopeItem.id,
documentDataId: envelopeItem.documentDataId,
diff --git a/apps/remix/app/components/embed/embed-direct-template-client-page.tsx b/apps/remix/app/components/embed/embed-direct-template-client-page.tsx
index 6b02a3b03..0add3413d 100644
--- a/apps/remix/app/components/embed/embed-direct-template-client-page.tsx
+++ b/apps/remix/app/components/embed/embed-direct-template-client-page.tsx
@@ -19,11 +19,12 @@ import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn'
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
+import { ZDirectTemplateEmbedDataSchema } from '@documenso/lib/types/embed-direct-template-schema';
import {
isFieldUnsignedAndRequired,
isRequiredField,
} from '@documenso/lib/utils/advanced-fields-helpers';
-import { getDocumentDataUrl } from '@documenso/lib/utils/envelope-download';
+import { getDocumentDataUrlForPdfViewer } from '@documenso/lib/utils/envelope-download';
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
import { trpc } from '@documenso/trpc/react';
@@ -41,7 +42,6 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
import { BrandingLogo } from '~/components/general/branding-logo';
import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer';
-import { ZDirectTemplateEmbedDataSchema } from '~/types/embed-direct-template-schema';
import { injectCss } from '~/utils/css-vars';
import type { DirectTemplateLocalField } from '../general/direct-template/direct-template-signing-form';
@@ -341,10 +341,10 @@ export const EmbedDirectTemplateClientPage = ({
{/* Viewer */}
@@ -310,7 +319,8 @@ export const EditorFieldCheckboxForm = ({
@@ -325,7 +335,11 @@ export const EditorFieldCheckboxForm = ({
render={({ field }) => (
-
+
)}
@@ -333,6 +347,7 @@ export const EditorFieldCheckboxForm = ({
removeValue(index)}
>
diff --git a/apps/remix/app/components/forms/editor/editor-field-dropdown-form.tsx b/apps/remix/app/components/forms/editor/editor-field-dropdown-form.tsx
index ed4ae9ee9..bb127f4d0 100644
--- a/apps/remix/app/components/forms/editor/editor-field-dropdown-form.tsx
+++ b/apps/remix/app/components/forms/editor/editor-field-dropdown-form.tsx
@@ -176,7 +176,10 @@ export const EditorFieldDropdownForm = ({
value={field.value ?? '-1'}
onValueChange={(value) => field.onChange(value === '-1' ? undefined : value)}
>
-
+
@@ -215,7 +218,7 @@ export const EditorFieldDropdownForm = ({
Dropdown values
-
+
@@ -229,7 +232,7 @@ export const EditorFieldDropdownForm = ({
render={({ field }) => (
-
+
@@ -238,6 +241,7 @@ export const EditorFieldDropdownForm = ({
removeValue(index)}
>
diff --git a/apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx b/apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
index 3ba8c434a..3b7bca894 100644
--- a/apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
+++ b/apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
@@ -50,6 +50,7 @@ export const EditorGenericFontSizeField = ({
-
+
@@ -131,7 +132,7 @@ export const EditorGenericVerticalAlignField = ({
-
+
@@ -174,6 +175,7 @@ export const EditorGenericLineHeightField = ({
-
@@ -293,12 +297,13 @@ export const EditorGenericReadOnlyField = ({
-
+
Read Only
@@ -329,7 +334,7 @@ export const EditorGenericLabelField = ({
Label
-
+
diff --git a/apps/remix/app/components/forms/editor/editor-field-number-form.tsx b/apps/remix/app/components/forms/editor/editor-field-number-form.tsx
index ed4598324..7697e7137 100644
--- a/apps/remix/app/components/forms/editor/editor-field-number-form.tsx
+++ b/apps/remix/app/components/forms/editor/editor-field-number-form.tsx
@@ -167,7 +167,12 @@ export const EditorFieldNumberForm = ({
Placeholder
-
+
@@ -183,7 +188,12 @@ export const EditorFieldNumberForm = ({
Value
-
+
@@ -203,7 +213,10 @@ export const EditorFieldNumberForm = ({
value={field.value === null ? '-1' : field.value}
onValueChange={(value) => field.onChange(value === '-1' ? null : value)}
>
-
+
@@ -257,6 +270,7 @@ export const EditorFieldNumberForm = ({
-
+
@@ -172,7 +175,7 @@ export const EditorFieldRadioForm = ({
Radio values
-
+
@@ -187,7 +190,8 @@ export const EditorFieldRadioForm = ({
{
// Uncheck all other values.
@@ -216,7 +220,11 @@ export const EditorFieldRadioForm = ({
render={({ field }) => (
-
+
)}
@@ -224,6 +232,7 @@ export const EditorFieldRadioForm = ({
removeValue(index)}
>
diff --git a/apps/remix/app/components/forms/editor/editor-field-text-form.tsx b/apps/remix/app/components/forms/editor/editor-field-text-form.tsx
index 7f23de986..4be52e179 100644
--- a/apps/remix/app/components/forms/editor/editor-field-text-form.tsx
+++ b/apps/remix/app/components/forms/editor/editor-field-text-form.tsx
@@ -134,7 +134,7 @@ export const EditorFieldTextForm = ({
Label
-
+
@@ -150,7 +150,11 @@ export const EditorFieldTextForm = ({
Placeholder
-
+
@@ -167,6 +171,7 @@ export const EditorFieldTextForm = ({