includio-cms 0.5.1 → 0.5.3

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 (106) hide show
  1. package/CHANGELOG.md +35 -12
  2. package/ROADMAP.md +17 -0
  3. package/dist/admin/client/entry/entry-form.svelte +1 -0
  4. package/dist/admin/client/entry/entry.svelte +130 -123
  5. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +92 -9
  6. package/dist/admin/components/dashboard/changelog-dialog.svelte +16 -6
  7. package/dist/admin/components/fields/blocks-field.svelte +142 -112
  8. package/dist/admin/components/fields/blocks-field.svelte.d.ts +10 -30
  9. package/dist/admin/components/fields/boolean-field.svelte +28 -38
  10. package/dist/admin/components/fields/boolean-field.svelte.d.ts +5 -27
  11. package/dist/admin/components/fields/checkboxes-field.svelte +12 -24
  12. package/dist/admin/components/fields/checkboxes-field.svelte.d.ts +5 -27
  13. package/dist/admin/components/fields/content-field.svelte +4 -17
  14. package/dist/admin/components/fields/content-field.svelte.d.ts +5 -27
  15. package/dist/admin/components/fields/date-field.svelte +8 -21
  16. package/dist/admin/components/fields/date-field.svelte.d.ts +5 -27
  17. package/dist/admin/components/fields/datetime-field.svelte +8 -21
  18. package/dist/admin/components/fields/datetime-field.svelte.d.ts +5 -27
  19. package/dist/admin/components/fields/field-renderer.svelte +32 -19
  20. package/dist/admin/components/fields/field-renderer.svelte.d.ts +1 -1
  21. package/dist/admin/components/fields/field-value-bridge.svelte +21 -0
  22. package/dist/admin/components/fields/field-value-bridge.svelte.d.ts +31 -0
  23. package/dist/admin/components/fields/fields-form.svelte +13 -10
  24. package/dist/admin/components/fields/file-field.svelte +12 -27
  25. package/dist/admin/components/fields/file-field.svelte.d.ts +5 -27
  26. package/dist/admin/components/fields/image-field.svelte +13 -28
  27. package/dist/admin/components/fields/image-field.svelte.d.ts +5 -27
  28. package/dist/admin/components/fields/media-field.svelte +15 -30
  29. package/dist/admin/components/fields/media-field.svelte.d.ts +5 -27
  30. package/dist/admin/components/fields/number-field.svelte +6 -20
  31. package/dist/admin/components/fields/number-field.svelte.d.ts +5 -27
  32. package/dist/admin/components/fields/object-field.svelte +26 -29
  33. package/dist/admin/components/fields/object-field.svelte.d.ts +11 -31
  34. package/dist/admin/components/fields/radio-field.svelte +8 -20
  35. package/dist/admin/components/fields/radio-field.svelte.d.ts +5 -27
  36. package/dist/admin/components/fields/relation-field.svelte +15 -30
  37. package/dist/admin/components/fields/relation-field.svelte.d.ts +5 -27
  38. package/dist/admin/components/fields/richtext-field.svelte +4 -17
  39. package/dist/admin/components/fields/richtext-field.svelte.d.ts +5 -27
  40. package/dist/admin/components/fields/select-field.svelte +14 -28
  41. package/dist/admin/components/fields/select-field.svelte.d.ts +5 -27
  42. package/dist/admin/components/fields/seo-field.svelte +5 -12
  43. package/dist/admin/components/fields/seo-field.svelte.d.ts +8 -28
  44. package/dist/admin/components/fields/simple-array-field.svelte +29 -42
  45. package/dist/admin/components/fields/simple-array-field.svelte.d.ts +5 -27
  46. package/dist/admin/components/fields/slug-field.svelte +6 -11
  47. package/dist/admin/components/fields/slug-field.svelte.d.ts +6 -26
  48. package/dist/admin/components/fields/text-field-wrapper.svelte +22 -40
  49. package/dist/admin/components/fields/text-field.svelte +7 -19
  50. package/dist/admin/components/fields/text-field.svelte.d.ts +5 -27
  51. package/dist/admin/components/fields/url-field-wrapper.svelte +8 -3
  52. package/dist/admin/components/fields/url-field.svelte +294 -128
  53. package/dist/admin/components/fields/url-field.svelte.d.ts +5 -27
  54. package/dist/admin/components/layout/layout-renderer.svelte +8 -6
  55. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +221 -31
  56. package/dist/admin/components/tiptap/content-editor.svelte +13 -2
  57. package/dist/admin/components/tiptap/inline-block-node.d.ts +1 -0
  58. package/dist/admin/components/tiptap/inline-block-node.js +18 -1
  59. package/dist/admin/components/tiptap/slash-command.js +2 -3
  60. package/dist/admin/components/tiptap/standalone-form.d.ts +7 -0
  61. package/dist/admin/components/tiptap/standalone-form.js +31 -0
  62. package/dist/admin/components/tiptap/tiptap-editor.svelte +7 -0
  63. package/dist/admin/remote/entry.remote.js +16 -0
  64. package/dist/admin/styles/admin.css +10 -0
  65. package/dist/admin/utils/fieldCondition.d.ts +6 -0
  66. package/dist/admin/utils/fieldCondition.js +20 -0
  67. package/dist/components/ui/switch/index.d.ts +2 -0
  68. package/dist/components/ui/switch/index.js +4 -0
  69. package/dist/components/ui/switch/switch.svelte +26 -0
  70. package/dist/components/ui/switch/switch.svelte.d.ts +4 -0
  71. package/dist/core/fields/fieldSchemaToTs.js +15 -3
  72. package/dist/core/fields/formFieldSchemaToTs.js +22 -6
  73. package/dist/core/fields/urlUtils.d.ts +14 -0
  74. package/dist/core/fields/urlUtils.js +21 -0
  75. package/dist/core/server/fields/populateEntry.js +43 -0
  76. package/dist/core/server/fields/resolveImageFields.js +33 -1
  77. package/dist/core/server/fields/resolveRelationFields.js +46 -0
  78. package/dist/core/server/fields/resolveRichtextLinks.js +15 -1
  79. package/dist/core/server/fields/resolveUrlFields.js +65 -0
  80. package/dist/core/server/generator/formFieldSchemaToString.js +40 -9
  81. package/dist/core/server/generator/formFields.js +2 -0
  82. package/dist/core/server/generator/generator.js +25 -1
  83. package/dist/schemas/field/url.d.ts +2 -0
  84. package/dist/schemas/field/url.js +4 -2
  85. package/dist/types/fields.d.ts +9 -0
  86. package/dist/types/formFields.d.ts +15 -2
  87. package/dist/types/index.d.ts +1 -0
  88. package/dist/types/index.js +1 -0
  89. package/dist/updates/0.0.65/index.js +1 -1
  90. package/dist/updates/0.0.67/index.js +1 -1
  91. package/dist/updates/0.1.2/index.js +1 -1
  92. package/dist/updates/0.1.5/index.js +1 -1
  93. package/dist/updates/0.2.0/index.js +1 -1
  94. package/dist/updates/0.2.2/index.js +1 -1
  95. package/dist/updates/0.5.0/index.js +1 -1
  96. package/dist/updates/0.5.2/index.d.ts +2 -0
  97. package/dist/updates/0.5.2/index.js +14 -0
  98. package/dist/updates/0.5.3/index.d.ts +2 -0
  99. package/dist/updates/0.5.3/index.js +19 -0
  100. package/dist/updates/index.d.ts +2 -1
  101. package/dist/updates/index.js +3 -1
  102. package/package.json +2 -1
  103. package/dist/admin/components/fields/standalone-field-renderer.svelte +0 -148
  104. package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +0 -9
  105. package/dist/updates/0.0.65/migration.sql +0 -55
  106. package/dist/updates/0.0.67/migration.sql +0 -9
@@ -7,6 +7,7 @@ export {} from './config.js';
7
7
  export {} from './collections.js';
8
8
  export {} from './singles.js';
9
9
  export {} from './forms.js';
10
+ export {} from './formFields.js';
10
11
  export {} from './cms.js';
11
12
  export {} from './languages.js';
12
13
  export {} from './layout.js';
@@ -10,7 +10,7 @@ export const update = {
10
10
  ],
11
11
  fixes: [],
12
12
  breakingChanges: ['Media folders replaced with tags — existing folder assignments will be lost'],
13
- migration: `-- Move publish logic from entry_version to entry
13
+ sql: `-- Move publish logic from entry_version to entry
14
14
 
15
15
  ALTER TABLE entry ADD COLUMN published_at TIMESTAMP;
16
16
  ALTER TABLE entry ADD COLUMN published_version_id UUID
@@ -33,7 +33,7 @@ export const update = {
33
33
  breakingChanges: [
34
34
  'getImageStyles() now returns { styles, blurDataUrl } instead of plain styles record'
35
35
  ],
36
- migration: `ALTER TABLE image_styles ADD COLUMN IF NOT EXISTS quality INTEGER;
36
+ sql: `ALTER TABLE image_styles ADD COLUMN IF NOT EXISTS quality INTEGER;
37
37
  ALTER TABLE media_file ADD COLUMN IF NOT EXISTS blur_data_url TEXT;
38
38
  ALTER TABLE media_file ADD COLUMN IF NOT EXISTS focal_x REAL;
39
39
  ALTER TABLE media_file ADD COLUMN IF NOT EXISTS focal_y REAL;`
@@ -21,7 +21,7 @@ export const update = {
21
21
  ],
22
22
  fixes: [],
23
23
  breakingChanges: [],
24
- migration: `CREATE TABLE IF NOT EXISTS invitation (
24
+ sql: `CREATE TABLE IF NOT EXISTS invitation (
25
25
  id TEXT PRIMARY KEY,
26
26
  email TEXT NOT NULL,
27
27
  role TEXT NOT NULL DEFAULT 'user',
@@ -14,5 +14,5 @@ export const update = {
14
14
  'Pruning no longer deletes important versions to make room for empty ones'
15
15
  ],
16
16
  breakingChanges: [],
17
- migration: 'Removes duplicate entry versions where data is identical to the previous version. Preserves published, scheduled, and latest draft versions.'
17
+ notes: 'Removes duplicate entry versions where data is identical to the previous version. Preserves published, scheduled, and latest draft versions.'
18
18
  };
@@ -7,5 +7,5 @@ export const update = {
7
7
  breakingChanges: [
8
8
  'type:"array" with objects renamed to type:"blocks". Update collection/single configs.'
9
9
  ],
10
- migration: 'Config-only: rename type:"array" to type:"blocks" in field definitions. Stored data unchanged.'
10
+ notes: 'Config-only: rename type:"array" to type:"blocks" in field definitions. Stored data unchanged.'
11
11
  };
@@ -9,5 +9,5 @@ export const update = {
9
9
  ],
10
10
  fixes: [],
11
11
  breakingChanges: [],
12
- migration: 'No migration needed. Add type:"content" fields to collection/single configs. Existing richtext fields unchanged.'
12
+ notes: 'No migration needed. Add type:"content" fields to collection/single configs. Existing richtext fields unchanged.'
13
13
  };
@@ -10,5 +10,5 @@ export const update = {
10
10
  ],
11
11
  fixes: [],
12
12
  breakingChanges: [],
13
- migration: 'No migration needed. Import `StructuredContent` from `includio/sveltekit` to render content fields.'
13
+ notes: 'No migration needed. Import `StructuredContent` from `includio/sveltekit` to render content fields.'
14
14
  };
@@ -0,0 +1,2 @@
1
+ import type { CmsUpdate } from '../index.js';
2
+ export declare const update: CmsUpdate;
@@ -0,0 +1,14 @@
1
+ export const update = {
2
+ version: '0.5.2',
3
+ date: '2026-02-25',
4
+ description: 'Update system: split migration into sql + notes',
5
+ features: [],
6
+ fixes: [
7
+ 'CmsUpdate.migration split into sql (executable SQL) and notes (manual steps) — fixes CLI crash on text descriptions',
8
+ 'Changelog dialog: SQL accordion hidden when no SQL, notes shown as inline text',
9
+ 'Changelog script: SQL in ```sql blocks, notes as plain text'
10
+ ],
11
+ breakingChanges: [
12
+ 'CmsUpdate interface: migration field replaced by sql + notes fields'
13
+ ]
14
+ };
@@ -0,0 +1,2 @@
1
+ import type { CmsUpdate } from '../index.js';
2
+ export declare const update: CmsUpdate;
@@ -0,0 +1,19 @@
1
+ export const update = {
2
+ version: '0.5.3',
3
+ date: '2026-03-04',
4
+ description: 'Form fields, inline blocks, hybrid preview, field components refactor',
5
+ features: [
6
+ 'Form fields: select type, minLength/maxLength, errorMessage, remote commands',
7
+ 'Conditional field visibility (showWhen)',
8
+ 'Hybrid preview: device frames, container queries',
9
+ 'TipTap placeholder extension + Notion-style styles',
10
+ 'Server-side field resolution for inline blocks',
11
+ 'Inline blocks: standalone form, full field rendering, collapse UI',
12
+ 'URL field: rel attribute, external auto-detect, UI redesign',
13
+ 'Switch UI component (bits-ui)',
14
+ 'Boolean field: Switch toggle zamiast checkbox',
15
+ 'Storybook stories for all field types'
16
+ ],
17
+ fixes: ['Hybrid preview layout fix'],
18
+ breakingChanges: []
19
+ };
@@ -5,7 +5,8 @@ export interface CmsUpdate {
5
5
  features: string[];
6
6
  fixes: string[];
7
7
  breakingChanges: string[];
8
- migration?: string;
8
+ sql?: string;
9
+ notes?: string;
9
10
  }
10
11
  export declare const updates: CmsUpdate[];
11
12
  export declare const getUpdatesFrom: (fromVersion: string) => CmsUpdate[];
@@ -13,7 +13,9 @@ import { update as update020 } from './0.2.0/index.js';
13
13
  import { update as update022 } from './0.2.2/index.js';
14
14
  import { update as update050 } from './0.5.0/index.js';
15
15
  import { update as update051 } from './0.5.1/index.js';
16
- export const updates = [update0065, update0066, update0067, update0068, update0069, update010, update011, update012, update013, update014, update015, update020, update022, update050, update051];
16
+ import { update as update052 } from './0.5.2/index.js';
17
+ import { update as update053 } from './0.5.3/index.js';
18
+ export const updates = [update0065, update0066, update0067, update0068, update0069, update010, update011, update012, update013, update014, update015, update020, update022, update050, update051, update052, update053];
17
19
  export const getUpdatesFrom = (fromVersion) => {
18
20
  const fromParts = fromVersion.split('.').map(Number);
19
21
  return updates.filter((update) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "includio-cms",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -199,6 +199,7 @@
199
199
  "@tiptap/extension-highlight": "^3.17.1",
200
200
  "@tiptap/extension-image": "^3.17.1",
201
201
  "@tiptap/extension-link": "^3.17.1",
202
+ "@tiptap/extension-placeholder": "^3.20.0",
202
203
  "@tiptap/extension-table": "^3.17.1",
203
204
  "@tiptap/extension-table-cell": "^3.17.1",
204
205
  "@tiptap/extension-table-header": "^3.17.1",
@@ -1,148 +0,0 @@
1
- <script lang="ts">
2
- import type { Field } from '../../../types/fields.js';
3
- import Input from '../../../components/ui/input/input.svelte';
4
- import { Checkbox } from '../../../components/ui/checkbox/index.js';
5
- import * as Select from '../../../components/ui/select/index.js';
6
- import Label from '../../../components/ui/label/label.svelte';
7
- import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
8
- import { getLocalizedLabel } from '../../utils/collectionLabel.js';
9
-
10
- type Props = {
11
- fields: Field[];
12
- data: Record<string, unknown>;
13
- onchange: (slug: string, value: unknown) => void;
14
- };
15
-
16
- let { fields, data, onchange }: Props = $props();
17
-
18
- const interfaceLanguage = useInterfaceLanguage();
19
-
20
- const SUPPORTED_TYPES = new Set([
21
- 'text',
22
- 'number',
23
- 'boolean',
24
- 'select',
25
- 'radio',
26
- 'url'
27
- ]);
28
-
29
- function handleInput(slug: string, e: Event) {
30
- const target = e.target as HTMLInputElement;
31
- onchange(slug, target.value);
32
- }
33
-
34
- function handleNumberInput(slug: string, e: Event) {
35
- const target = e.target as HTMLInputElement;
36
- const num = parseFloat(target.value);
37
- onchange(slug, isNaN(num) ? 0 : num);
38
- }
39
- </script>
40
-
41
- <div class="grid gap-3">
42
- {#each fields as field}
43
- {#if SUPPORTED_TYPES.has(field.type)}
44
- <div class="space-y-1.5">
45
- {#if field.type !== 'boolean'}
46
- <Label class="text-xs font-medium">
47
- {getLocalizedLabel(field.label, interfaceLanguage.current)}
48
- {#if field.required}<span class="text-destructive">*</span>{/if}
49
- </Label>
50
- {/if}
51
-
52
- {#if field.type === 'text'}
53
- <Input
54
- type="text"
55
- value={String(data[field.slug] ?? '')}
56
- oninput={(e) => handleInput(field.slug, e)}
57
- placeholder={field.placeholder ? getLocalizedLabel(field.placeholder, interfaceLanguage.current) : ''}
58
- class="h-8 text-sm"
59
- />
60
- {:else if field.type === 'number'}
61
- <Input
62
- type="number"
63
- value={String(data[field.slug] ?? 0)}
64
- oninput={(e) => handleNumberInput(field.slug, e)}
65
- min={field.min}
66
- max={field.max}
67
- step={field.step}
68
- class="h-8 text-sm"
69
- />
70
- {:else if field.type === 'boolean'}
71
- <div class="flex items-center gap-2">
72
- <Checkbox
73
- checked={Boolean(data[field.slug])}
74
- onCheckedChange={(checked) => onchange(field.slug, checked)}
75
- />
76
- <Label class="text-xs font-medium">
77
- {getLocalizedLabel(field.label, interfaceLanguage.current)}
78
- </Label>
79
- </div>
80
- {:else if field.type === 'select' || field.type === 'radio'}
81
- {@const currentValue = String(data[field.slug] ?? '')}
82
- {@const currentLabel = field.options.find((o) => o.value === currentValue)}
83
- <Select.Root
84
- type="single"
85
- value={currentValue}
86
- onValueChange={(v) => onchange(field.slug, v)}
87
- >
88
- <Select.Trigger size="sm" class="w-full">
89
- {#if currentLabel}
90
- {getLocalizedLabel(currentLabel.label, interfaceLanguage.current)}
91
- {:else}
92
- <span class="text-muted-foreground">Wybierz...</span>
93
- {/if}
94
- </Select.Trigger>
95
- <Select.Content>
96
- {#each field.options as option}
97
- <Select.Item value={option.value} label={getLocalizedLabel(option.label, interfaceLanguage.current)} />
98
- {/each}
99
- </Select.Content>
100
- </Select.Root>
101
- {:else if field.type === 'url'}
102
- {@const urlData = (data[field.slug] as { url?: string; id?: string; text?: string; newTab?: boolean } | undefined) ?? { url: '' }}
103
- <Input
104
- type="url"
105
- value={urlData.url ?? ''}
106
- oninput={(e) => {
107
- const target = e.target as HTMLInputElement;
108
- onchange(field.slug, { ...urlData, url: target.value });
109
- }}
110
- placeholder={field.placeholder ? getLocalizedLabel(field.placeholder, interfaceLanguage.current) : 'https://...'}
111
- class="h-8 text-sm"
112
- />
113
- {#if field.text}
114
- <Input
115
- type="text"
116
- value={urlData.text ?? ''}
117
- oninput={(e) => {
118
- const target = e.target as HTMLInputElement;
119
- onchange(field.slug, { ...urlData, text: target.value });
120
- }}
121
- placeholder="Tekst linku"
122
- class="h-8 text-sm mt-1"
123
- />
124
- {/if}
125
- {#if field.newTab}
126
- <div class="flex items-center gap-2 mt-1">
127
- <Checkbox
128
- checked={urlData.newTab ?? false}
129
- onCheckedChange={(checked) => onchange(field.slug, { ...urlData, newTab: checked })}
130
- />
131
- <Label class="text-xs font-medium">Otwórz w nowej karcie</Label>
132
- </div>
133
- {/if}
134
- {/if}
135
-
136
- {#if field.description}
137
- <p class="text-muted-foreground text-xs">
138
- {getLocalizedLabel(field.description, interfaceLanguage.current)}
139
- </p>
140
- {/if}
141
- </div>
142
- {:else}
143
- <div class="text-muted-foreground rounded-md border border-dashed p-2 text-center text-xs">
144
- Pole „{getLocalizedLabel(field.label, interfaceLanguage.current)}" ({field.type}) nie jest obsługiwane w blokach inline
145
- </div>
146
- {/if}
147
- {/each}
148
- </div>
@@ -1,9 +0,0 @@
1
- import type { Field } from '../../../types/fields.js';
2
- type Props = {
3
- fields: Field[];
4
- data: Record<string, unknown>;
5
- onchange: (slug: string, value: unknown) => void;
6
- };
7
- declare const StandaloneFieldRenderer: import("svelte").Component<Props, {}, "">;
8
- type StandaloneFieldRenderer = ReturnType<typeof StandaloneFieldRenderer>;
9
- export default StandaloneFieldRenderer;
@@ -1,55 +0,0 @@
1
- -- Move publish logic from entry_version to entry
2
-
3
- ALTER TABLE entry ADD COLUMN published_at TIMESTAMP;
4
- ALTER TABLE entry ADD COLUMN published_version_id UUID
5
- REFERENCES entry_version(id) ON DELETE SET NULL;
6
- ALTER TABLE entry ADD COLUMN published_by TEXT;
7
-
8
- UPDATE entry e SET
9
- published_version_id = latest.id,
10
- published_at = first_pub.first_published_at,
11
- published_by = latest.published_by
12
- FROM (
13
- SELECT DISTINCT ON (entry_id) id, entry_id, published_by
14
- FROM entry_version
15
- WHERE published_at IS NOT NULL AND published_at <= NOW()
16
- ORDER BY entry_id, version_number DESC
17
- ) latest
18
- JOIN (
19
- SELECT entry_id, MIN(published_at) as first_published_at
20
- FROM entry_version
21
- WHERE published_at IS NOT NULL AND published_at <= NOW()
22
- GROUP BY entry_id
23
- ) first_pub ON first_pub.entry_id = latest.entry_id
24
- WHERE e.id = latest.entry_id;
25
-
26
- UPDATE entry e SET
27
- published_version_id = COALESCE(e.published_version_id, sub.id),
28
- published_at = CASE WHEN e.published_at IS NULL THEN sub.published_at ELSE e.published_at END,
29
- published_by = COALESCE(e.published_by, sub.published_by)
30
- FROM (
31
- SELECT DISTINCT ON (entry_id) id, entry_id, published_at, published_by
32
- FROM entry_version
33
- WHERE published_at IS NOT NULL AND published_at > NOW()
34
- ORDER BY entry_id, published_at ASC
35
- ) sub
36
- WHERE e.id = sub.entry_id AND e.published_version_id IS NULL;
37
-
38
- -- Replace folder-based media with tag-based media
39
-
40
- CREATE TABLE IF NOT EXISTS media_tag (
41
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
42
- name TEXT NOT NULL UNIQUE,
43
- color TEXT NOT NULL DEFAULT '#3b82f6',
44
- created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
45
- );
46
-
47
- CREATE TABLE IF NOT EXISTS media_file_tag (
48
- file_id UUID NOT NULL REFERENCES media_file(id) ON DELETE CASCADE,
49
- tag_id UUID NOT NULL REFERENCES media_tag(id) ON DELETE CASCADE,
50
- PRIMARY KEY (file_id, tag_id)
51
- );
52
-
53
- ALTER TABLE media_file DROP COLUMN IF EXISTS folder_id;
54
-
55
- DROP TABLE IF EXISTS media_folder;
@@ -1,9 +0,0 @@
1
- -- Add quality control to image styles
2
- ALTER TABLE image_styles ADD COLUMN IF NOT EXISTS quality INTEGER;
3
-
4
- -- Add LQIP blur placeholder to media files
5
- ALTER TABLE media_file ADD COLUMN IF NOT EXISTS blur_data_url TEXT;
6
-
7
- -- Add focal point columns to media files
8
- ALTER TABLE media_file ADD COLUMN IF NOT EXISTS focal_x REAL;
9
- ALTER TABLE media_file ADD COLUMN IF NOT EXISTS focal_y REAL;