@firecms/collection_editor 3.0.1 → 3.1.0-canary.1df3b2c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/ConfigControllerProvider.d.ts +6 -0
  2. package/dist/api/generateCollectionApi.d.ts +71 -0
  3. package/dist/api/index.d.ts +1 -0
  4. package/dist/index.d.ts +5 -1
  5. package/dist/index.es.js +9418 -5587
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +9413 -5582
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/types/collection_editor_controller.d.ts +14 -0
  10. package/dist/types/collection_inference.d.ts +8 -2
  11. package/dist/types/config_controller.d.ts +23 -2
  12. package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
  13. package/dist/ui/KanbanSetupAction.d.ts +10 -0
  14. package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +33 -0
  15. package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
  16. package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
  17. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +20 -0
  18. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +3 -1
  19. package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
  20. package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
  21. package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
  22. package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
  23. package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
  24. package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
  25. package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
  26. package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
  27. package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
  28. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
  29. package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
  30. package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
  31. package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
  32. package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
  33. package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
  34. package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
  35. package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
  36. package/dist/useCollectionEditorPlugin.d.ts +7 -1
  37. package/dist/utils/validateCollectionJson.d.ts +22 -0
  38. package/package.json +11 -11
  39. package/src/ConfigControllerProvider.tsx +81 -47
  40. package/src/api/generateCollectionApi.ts +119 -0
  41. package/src/api/index.ts +1 -0
  42. package/src/index.ts +28 -1
  43. package/src/types/collection_editor_controller.tsx +16 -3
  44. package/src/types/collection_inference.ts +15 -2
  45. package/src/types/config_controller.tsx +27 -2
  46. package/src/ui/AddKanbanColumnAction.tsx +203 -0
  47. package/src/ui/EditorCollectionActionStart.tsx +1 -2
  48. package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
  49. package/src/ui/KanbanSetupAction.tsx +38 -0
  50. package/src/ui/MissingReferenceWidget.tsx +1 -1
  51. package/src/ui/NewCollectionButton.tsx +1 -1
  52. package/src/ui/PropertyAddColumnComponent.tsx +1 -1
  53. package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +225 -0
  54. package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
  55. package/src/ui/collection_editor/CollectionDetailsForm.tsx +209 -257
  56. package/src/ui/collection_editor/CollectionEditorDialog.tsx +226 -167
  57. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +130 -67
  58. package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
  59. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +190 -91
  60. package/src/ui/collection_editor/DisplaySettingsForm.tsx +333 -0
  61. package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -96
  62. package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +6 -7
  63. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +1 -3
  64. package/src/ui/collection_editor/EnumForm.tsx +147 -100
  65. package/src/ui/collection_editor/ExtendSettingsForm.tsx +93 -0
  66. package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
  67. package/src/ui/collection_editor/GetCodeDialog.tsx +57 -36
  68. package/src/ui/collection_editor/KanbanConfigSection.tsx +207 -0
  69. package/src/ui/collection_editor/LayoutModeSwitch.tsx +22 -41
  70. package/src/ui/collection_editor/PropertyEditView.tsx +205 -141
  71. package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
  72. package/src/ui/collection_editor/PropertyTree.tsx +130 -58
  73. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +171 -162
  74. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
  75. package/src/ui/collection_editor/ViewModeSwitch.tsx +41 -0
  76. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
  77. package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +1 -0
  78. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +117 -35
  79. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +28 -21
  80. package/src/ui/collection_editor/properties/MapPropertyField.tsx +0 -2
  81. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +115 -39
  82. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +1 -1
  83. package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +861 -0
  84. package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
  85. package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
  86. package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
  87. package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
  88. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +1 -1
  89. package/src/useCollectionEditorPlugin.tsx +32 -17
  90. package/src/utils/validateCollectionJson.ts +380 -0
@@ -5,6 +5,59 @@ import { Select, SelectItem } from "@firecms/ui";
5
5
  import { GeneralPropertyValidation } from "./validation/GeneralPropertyValidation";
6
6
  import { ValidationPanel } from "./validation/ValidationPanel";
7
7
 
8
+ // Common IANA timezones with human-readable labels
9
+ const TIMEZONES = [
10
+ // UTC
11
+ { value: "UTC", label: "UTC (Coordinated Universal Time)" },
12
+ // Americas
13
+ { value: "America/New_York", label: "New York (US Eastern)" },
14
+ { value: "America/Chicago", label: "Chicago (US Central)" },
15
+ { value: "America/Denver", label: "Denver (US Mountain)" },
16
+ { value: "America/Los_Angeles", label: "Los Angeles (US Pacific)" },
17
+ { value: "America/Anchorage", label: "Anchorage (Alaska)" },
18
+ { value: "America/Toronto", label: "Toronto (Canada Eastern)" },
19
+ { value: "America/Vancouver", label: "Vancouver (Canada Pacific)" },
20
+ { value: "America/Mexico_City", label: "Mexico City" },
21
+ { value: "America/Sao_Paulo", label: "São Paulo (Brazil)" },
22
+ { value: "America/Buenos_Aires", label: "Buenos Aires (Argentina)" },
23
+ // Europe
24
+ { value: "Europe/London", label: "London (UK)" },
25
+ { value: "Europe/Paris", label: "Paris (France)" },
26
+ { value: "Europe/Berlin", label: "Berlin (Germany)" },
27
+ { value: "Europe/Madrid", label: "Madrid (Spain)" },
28
+ { value: "Europe/Rome", label: "Rome (Italy)" },
29
+ { value: "Europe/Amsterdam", label: "Amsterdam (Netherlands)" },
30
+ { value: "Europe/Brussels", label: "Brussels (Belgium)" },
31
+ { value: "Europe/Zurich", label: "Zurich (Switzerland)" },
32
+ { value: "Europe/Stockholm", label: "Stockholm (Sweden)" },
33
+ { value: "Europe/Vienna", label: "Vienna (Austria)" },
34
+ { value: "Europe/Warsaw", label: "Warsaw (Poland)" },
35
+ { value: "Europe/Prague", label: "Prague (Czech Republic)" },
36
+ { value: "Europe/Athens", label: "Athens (Greece)" },
37
+ { value: "Europe/Moscow", label: "Moscow (Russia)" },
38
+ { value: "Europe/Istanbul", label: "Istanbul (Turkey)" },
39
+ // Asia
40
+ { value: "Asia/Dubai", label: "Dubai (UAE)" },
41
+ { value: "Asia/Kolkata", label: "Mumbai / Delhi (India)" },
42
+ { value: "Asia/Bangkok", label: "Bangkok (Thailand)" },
43
+ { value: "Asia/Singapore", label: "Singapore" },
44
+ { value: "Asia/Hong_Kong", label: "Hong Kong" },
45
+ { value: "Asia/Shanghai", label: "Shanghai (China)" },
46
+ { value: "Asia/Tokyo", label: "Tokyo (Japan)" },
47
+ { value: "Asia/Seoul", label: "Seoul (South Korea)" },
48
+ { value: "Asia/Jakarta", label: "Jakarta (Indonesia)" },
49
+ // Oceania
50
+ { value: "Australia/Sydney", label: "Sydney (Australia Eastern)" },
51
+ { value: "Australia/Melbourne", label: "Melbourne (Australia)" },
52
+ { value: "Australia/Brisbane", label: "Brisbane (Australia)" },
53
+ { value: "Australia/Perth", label: "Perth (Australia Western)" },
54
+ { value: "Pacific/Auckland", label: "Auckland (New Zealand)" },
55
+ // Africa
56
+ { value: "Africa/Cairo", label: "Cairo (Egypt)" },
57
+ { value: "Africa/Johannesburg", label: "Johannesburg (South Africa)" },
58
+ { value: "Africa/Lagos", label: "Lagos (Nigeria)" },
59
+ ];
60
+
8
61
  export function DateTimePropertyField({ disabled }: {
9
62
  disabled: boolean;
10
63
  }) {
@@ -24,28 +77,32 @@ export function DateTimePropertyField({ disabled }: {
24
77
  const autoValueValue: string | undefined = getIn(values, autoValuePath);
25
78
  const autoValueError: string | undefined = getIn(touched, autoValuePath) && getIn(errors, autoValuePath);
26
79
 
80
+ const timezonePath = "timezone";
81
+ const timezoneValue: string | undefined = getIn(values, timezonePath);
82
+ const timezoneError: string | undefined = getIn(touched, timezonePath) && getIn(errors, timezonePath);
83
+
27
84
  return (
28
85
  <>
29
86
  <div className={"flex flex-col col-span-12 gap-2"}>
30
87
  <div>
31
88
  <Select name={modePath}
32
- value={modeValue ?? "date"}
33
- error={Boolean(modeError)}
34
- size={"large"}
35
- onValueChange={(v) => setFieldValue(modePath, v)}
36
- label={"Mode"}
37
- fullWidth={true}
38
- renderValue={(v) => {
39
- switch (v) {
40
- case "date_time":
41
- return "Date/Time";
42
- case "date":
43
- return "Date";
44
- default:
45
- return "";
46
- }
47
- }}
48
- disabled={disabled}>
89
+ value={modeValue ?? "date"}
90
+ error={Boolean(modeError)}
91
+ size={"large"}
92
+ onValueChange={(v) => setFieldValue(modePath, v)}
93
+ label={"Mode"}
94
+ fullWidth={true}
95
+ renderValue={(v) => {
96
+ switch (v) {
97
+ case "date_time":
98
+ return "Date/Time";
99
+ case "date":
100
+ return "Date";
101
+ default:
102
+ return "";
103
+ }
104
+ }}
105
+ disabled={disabled}>
49
106
  <SelectItem value={"date_time"}> Date/Time </SelectItem>
50
107
  <SelectItem value={"date"}> Date </SelectItem>
51
108
  </Select>
@@ -55,23 +112,23 @@ export function DateTimePropertyField({ disabled }: {
55
112
  </div>
56
113
  <div>
57
114
  <Select name={autoValuePath}
58
- disabled={disabled}
59
- size={"large"}
60
- fullWidth={true}
61
- value={autoValueValue ?? ""}
62
- onValueChange={(v) => setFieldValue(autoValuePath, v === "none" ? null : v)}
63
- renderValue={(v) => {
64
- switch (v) {
65
- case "on_create":
66
- return "On create";
67
- case "on_update":
68
- return "On any update";
69
- default:
70
- return "None";
71
- }
72
- }}
73
- error={Boolean(autoValueError)}
74
- label={"Automatic value"}>
115
+ disabled={disabled}
116
+ size={"large"}
117
+ fullWidth={true}
118
+ value={autoValueValue ?? ""}
119
+ onValueChange={(v) => setFieldValue(autoValuePath, v === "none" ? null : v)}
120
+ renderValue={(v) => {
121
+ switch (v) {
122
+ case "on_create":
123
+ return "On create";
124
+ case "on_update":
125
+ return "On any update";
126
+ default:
127
+ return "None";
128
+ }
129
+ }}
130
+ error={Boolean(autoValueError)}
131
+ label={"Automatic value"}>
75
132
  <SelectItem value={"none"}> None </SelectItem>
76
133
  <SelectItem value={"on_create"}> On create </SelectItem>
77
134
  <SelectItem value={"on_update"}> On any update </SelectItem>
@@ -80,12 +137,37 @@ export function DateTimePropertyField({ disabled }: {
80
137
  {autoValueError ?? "Update this field automatically when creating or updating the entity"}
81
138
  </FieldCaption>
82
139
  </div>
140
+ <div>
141
+ <Select name={timezonePath}
142
+ disabled={disabled}
143
+ size={"large"}
144
+ fullWidth={true}
145
+ value={timezoneValue ?? "__local__"}
146
+ onValueChange={(v) => setFieldValue(timezonePath, v === "__local__" ? undefined : v)}
147
+ renderValue={(v) => {
148
+ if (!v || v === "__local__") return "Local timezone";
149
+ const tz = TIMEZONES.find(t => t.value === v);
150
+ return tz?.label ?? v;
151
+ }}
152
+ error={Boolean(timezoneError)}
153
+ label={"Timezone"}>
154
+ <SelectItem value={"__local__"}> Local timezone </SelectItem>
155
+ {TIMEZONES.map((tz) => (
156
+ <SelectItem key={tz.value} value={tz.value}>
157
+ {tz.label}
158
+ </SelectItem>
159
+ ))}
160
+ </Select>
161
+ <FieldCaption error={Boolean(timezoneError)}>
162
+ {timezoneError ?? "Timezone for displaying and inputting dates. Values are always stored in UTC."}
163
+ </FieldCaption>
164
+ </div>
83
165
 
84
166
  </div>
85
167
 
86
168
  <div className={"col-span-12"}>
87
169
  <ValidationPanel>
88
- <GeneralPropertyValidation disabled={disabled}/>
170
+ <GeneralPropertyValidation disabled={disabled} />
89
171
  </ValidationPanel>
90
172
  </div>
91
173
  </>
@@ -9,19 +9,21 @@ import { ValidationPanel } from "./validation/ValidationPanel";
9
9
  import { PropertyWithId } from "../PropertyEditView";
10
10
 
11
11
  export function EnumPropertyField({
12
- multiselect,
13
- updateIds,
14
- disabled,
15
- showErrors,
16
- allowDataInference,
17
- getData
18
- }: {
12
+ multiselect,
13
+ updateIds,
14
+ disabled,
15
+ showErrors,
16
+ allowDataInference,
17
+ getData,
18
+ propertyNamespace
19
+ }: {
19
20
  multiselect: boolean;
20
21
  updateIds: boolean;
21
22
  disabled: boolean;
22
23
  showErrors: boolean;
23
24
  allowDataInference?: boolean;
24
25
  getData?: () => Promise<object[]>;
26
+ propertyNamespace?: string;
25
27
  }) {
26
28
 
27
29
  const {
@@ -59,21 +61,26 @@ export function EnumPropertyField({
59
61
  }
60
62
  };
61
63
 
64
+ // Build full path including namespace for nested properties (e.g., "address.status")
65
+ const fullPropertyPath = values.id
66
+ ? (propertyNamespace ? `${propertyNamespace}.${values.id}` : values.id)
67
+ : undefined;
68
+
62
69
  return (
63
70
  <>
64
71
  <div className={"col-span-12"}>
65
72
  <EnumForm enumValues={enumValues}
66
- updateIds={updateIds}
67
- disabled={disabled}
68
- allowDataInference={allowDataInference}
69
- onError={(hasError) => {
70
- setFieldError(enumValuesPath, hasError ? "This enum property is missing some values" : undefined);
71
- }}
72
- getData={getData
73
- ? () => getData()
74
- .then(res => res.map(entry => values.id && getIn(entry, values.id)).filter(Boolean))
75
- : undefined}
76
- onValuesChanged={onValuesChanged}/>
73
+ updateIds={updateIds}
74
+ disabled={disabled}
75
+ allowDataInference={allowDataInference}
76
+ onError={(hasError) => {
77
+ setFieldError(enumValuesPath, hasError ? "This enum property is missing some values" : undefined);
78
+ }}
79
+ getData={getData && fullPropertyPath
80
+ ? () => getData()
81
+ .then(res => res.map(entry => getIn(entry, fullPropertyPath)).filter(Boolean))
82
+ : undefined}
83
+ onValuesChanged={onValuesChanged} />
77
84
  </div>
78
85
 
79
86
  <div className={"col-span-12"}>
@@ -81,9 +88,9 @@ export function EnumPropertyField({
81
88
  <ValidationPanel>
82
89
  {!multiselect &&
83
90
  <StringPropertyValidation disabled={disabled}
84
- showErrors={showErrors}/>}
91
+ showErrors={showErrors} />}
85
92
  {multiselect &&
86
- <ArrayPropertyValidation disabled={disabled}/>}
93
+ <ArrayPropertyValidation disabled={disabled} />}
87
94
  </ValidationPanel>
88
95
 
89
96
  </div>
@@ -104,7 +111,7 @@ export function EnumPropertyField({
104
111
  .filter((enumValue) => Boolean(enumValue?.id))
105
112
  .map((enumValue) => (
106
113
  <SelectItem key={enumValue.id}
107
- value={enumValue.id?.toString()}>
114
+ value={enumValue.id?.toString()}>
108
115
  {enumValue.label}
109
116
  </SelectItem>
110
117
  ))}
@@ -75,8 +75,6 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
75
75
  <div className="flex justify-between items-end my-4">
76
76
  <Typography variant={"subtitle2"}>Properties in this group</Typography>
77
77
  <Button
78
- color="primary"
79
- variant={"outlined"}
80
78
  onClick={() => setPropertyDialogOpen(true)}
81
79
  startIcon={<AddIcon/>}
82
80
  >
@@ -1,14 +1,22 @@
1
- import React from "react";
1
+ import React, { useCallback } from "react";
2
2
  import { StringPropertyValidation } from "./validation/StringPropertyValidation";
3
3
  import { ValidationPanel } from "./validation/ValidationPanel";
4
4
  import { Field, getIn, useFormex } from "@firecms/formex";
5
5
 
6
- import { CloudUploadIcon, DebouncedTextField, ExpandablePanel, TextField, Typography } from "@firecms/ui";
6
+ import {
7
+ BooleanSwitchWithLabel,
8
+ CloudUploadIcon,
9
+ DebouncedTextField,
10
+ ExpandablePanel,
11
+ SettingsIcon,
12
+ TextField,
13
+ Typography
14
+ } from "@firecms/ui";
7
15
 
8
16
  export function MarkdownPropertyField({
9
- disabled,
10
- showErrors
11
- }: {
17
+ disabled,
18
+ showErrors
19
+ }: {
12
20
  disabled: boolean;
13
21
  showErrors: boolean;
14
22
  }) {
@@ -19,19 +27,49 @@ export function MarkdownPropertyField({
19
27
  } = useFormex();
20
28
 
21
29
  const baseStoragePath = "storage";
30
+ const baseMarkdownPath = "markdown";
22
31
 
23
32
  const metadata = `${baseStoragePath}.metadata`;
24
33
  const fileName = `${baseStoragePath}.fileName`;
25
34
  const maxSize = `${baseStoragePath}.maxSize`;
26
35
  const storagePath = `${baseStoragePath}.storagePath`;
27
36
 
37
+ // Markdown config paths
38
+ const htmlPath = `${baseMarkdownPath}.html`;
39
+ const transformPastedTextPath = `${baseMarkdownPath}.transformPastedText`;
40
+
28
41
  const fileNameValue = getIn(values, fileName) ?? "{rand}_{file}";
29
42
  const storagePathValue = getIn(values, storagePath) ?? "/";
30
43
  const maxSizeValue = getIn(values, maxSize);
31
44
 
45
+ // Markdown config values - check if markdown is an object or boolean
46
+ const markdownValue = getIn(values, "markdown");
47
+ const isMarkdownObject = typeof markdownValue === "object" && markdownValue !== null;
48
+ const htmlValue = isMarkdownObject ? (markdownValue.html ?? true) : true;
49
+ const transformPastedTextValue = isMarkdownObject ? (markdownValue.transformPastedText ?? false) : false;
50
+
32
51
  const hasFilenameCallback = typeof fileNameValue === "function";
33
52
  const hasStoragePathCallback = typeof storagePathValue === "function";
34
53
 
54
+ // Handler to update markdown config - converts from boolean to object if needed
55
+ const updateMarkdownConfig = useCallback((key: "html" | "transformPastedText", value: boolean) => {
56
+ const currentMarkdown = getIn(values, "markdown");
57
+ if (typeof currentMarkdown === "object" && currentMarkdown !== null) {
58
+ // Already an object, update the key
59
+ setFieldValue(`markdown.${key}`, value);
60
+ } else {
61
+ // Convert from boolean to object
62
+ const defaultConfig = {
63
+ html: true,
64
+ transformPastedText: false
65
+ };
66
+ setFieldValue("markdown", {
67
+ ...defaultConfig,
68
+ [key]: value
69
+ });
70
+ }
71
+ }, [values, setFieldValue]);
72
+
35
73
  return (
36
74
  <>
37
75
  <div className={"col-span-12"}>
@@ -39,13 +77,13 @@ export function MarkdownPropertyField({
39
77
  <ValidationPanel>
40
78
 
41
79
  <StringPropertyValidation disabled={disabled}
42
- length={true}
43
- lowercase={true}
44
- max={true}
45
- min={true}
46
- trim={true}
47
- uppercase={true}
48
- showErrors={showErrors}/>
80
+ length={true}
81
+ lowercase={true}
82
+ max={true}
83
+ min={true}
84
+ trim={true}
85
+ uppercase={true}
86
+ showErrors={showErrors} />
49
87
 
50
88
  </ValidationPanel>
51
89
 
@@ -55,9 +93,47 @@ export function MarkdownPropertyField({
55
93
  <ExpandablePanel
56
94
  title={
57
95
  <div className="flex flex-row text-surface-500">
58
- <CloudUploadIcon/>
96
+ <SettingsIcon />
97
+ <Typography variant={"subtitle2"}
98
+ className="ml-4">
99
+ Paste behavior
100
+ </Typography>
101
+ </div>
102
+ }>
103
+
104
+ <div className={"flex flex-col gap-2 p-4"}>
105
+ <BooleanSwitchWithLabel
106
+ size={"small"}
107
+ disabled={disabled}
108
+ value={!htmlValue}
109
+ onValueChange={(value) => updateMarkdownConfig("html", !value)}
110
+ label={"Strip HTML on paste"}
111
+ />
112
+ <Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
113
+ Remove HTML tags and inline styles when pasting content from external sources
114
+ </Typography>
115
+
116
+ <BooleanSwitchWithLabel
117
+ size={"small"}
118
+ disabled={disabled}
119
+ value={transformPastedTextValue}
120
+ onValueChange={(value) => updateMarkdownConfig("transformPastedText", value)}
121
+ label={"Convert pasted text to markdown"}
122
+ />
123
+ <Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
124
+ Convert rich text (from Google Docs, Word, etc.) to clean markdown format
125
+ </Typography>
126
+ </div>
127
+ </ExpandablePanel>
128
+ </div>
129
+
130
+ <div className={"col-span-12"}>
131
+ <ExpandablePanel
132
+ title={
133
+ <div className="flex flex-row text-surface-500">
134
+ <CloudUploadIcon />
59
135
  <Typography variant={"subtitle2"}
60
- className="ml-4">
136
+ className="ml-4">
61
137
  File upload config
62
138
  </Typography>
63
139
  </div>
@@ -68,20 +144,20 @@ export function MarkdownPropertyField({
68
144
 
69
145
  <div className={"col-span-12"}>
70
146
  <Field name={fileName}
71
- as={DebouncedTextField}
72
- label={"File name"}
73
- size={"small"}
74
- disabled={hasFilenameCallback || disabled}
75
- value={hasFilenameCallback ? "-" : fileNameValue}
147
+ as={DebouncedTextField}
148
+ label={"File name"}
149
+ size={"small"}
150
+ disabled={hasFilenameCallback || disabled}
151
+ value={hasFilenameCallback ? "-" : fileNameValue}
76
152
  />
77
153
  </div>
78
154
  <div className={"col-span-12"}>
79
155
  <Field name={storagePath}
80
- as={DebouncedTextField}
81
- label={"Storage path"}
82
- disabled={hasStoragePathCallback || disabled}
83
- size={"small"}
84
- value={hasStoragePathCallback ? "-" : storagePathValue}
156
+ as={DebouncedTextField}
157
+ label={"Storage path"}
158
+ disabled={hasStoragePathCallback || disabled}
159
+ size={"small"}
160
+ value={hasStoragePathCallback ? "-" : storagePathValue}
85
161
  />
86
162
  <Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
87
163
  <p>You can use the following placeholders in
@@ -107,15 +183,15 @@ export function MarkdownPropertyField({
107
183
 
108
184
  <div className={"col-span-12"}>
109
185
  <DebouncedTextField name={maxSize}
110
- type={"number"}
111
- label={"Max size (in bytes)"}
112
- size={"small"}
113
- value={maxSizeValue !== undefined && maxSizeValue !== null ? maxSizeValue.toString() : ""}
114
- onChange={(e) => {
115
- const value = e.target.value;
116
- if (value === "") setFieldValue(maxSize, undefined);
117
- else setFieldValue(maxSize, parseInt(value));
118
- }}
186
+ type={"number"}
187
+ label={"Max size (in bytes)"}
188
+ size={"small"}
189
+ value={maxSizeValue !== undefined && maxSizeValue !== null ? maxSizeValue.toString() : ""}
190
+ onChange={(e) => {
191
+ const value = e.target.value;
192
+ if (value === "") setFieldValue(maxSize, undefined);
193
+ else setFieldValue(maxSize, parseInt(value));
194
+ }}
119
195
  />
120
196
  </div>
121
197
 
@@ -126,12 +202,12 @@ export function MarkdownPropertyField({
126
202
  <div className={"col-span-12"}>
127
203
 
128
204
  <TextField name={"defaultValue"}
129
- disabled={disabled}
130
- onChange={(e: any) => {
131
- setFieldValue("defaultValue", e.target.value === "" ? undefined : e.target.value);
132
- }}
133
- label={"Default value"}
134
- value={getIn(values, "defaultValue") ?? ""}/>
205
+ disabled={disabled}
206
+ onChange={(e: any) => {
207
+ setFieldValue("defaultValue", e.target.value === "" ? undefined : e.target.value);
208
+ }}
209
+ label={"Default value"}
210
+ value={getIn(values, "defaultValue") ?? ""} />
135
211
 
136
212
  </div>
137
213
  </>
@@ -88,7 +88,7 @@ export function StoragePropertyField({
88
88
 
89
89
  <ExpandablePanel
90
90
  title={
91
- <div className="flex flex-row text-surface-500">
91
+ <div className="flex flex-row text-surface-500 text-text-secondary dark:text-text-secondary-dark">
92
92
  <CloudUploadIcon/>
93
93
  <Typography variant={"subtitle2"}
94
94
  className="ml-4">