includio-cms 0.5.2 → 0.5.5

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 (132) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/ROADMAP.md +29 -0
  3. package/dist/admin/api/rest/handler.d.ts +7 -0
  4. package/dist/admin/api/rest/handler.js +116 -0
  5. package/dist/admin/api/rest/middleware/apiKey.d.ts +6 -0
  6. package/dist/admin/api/rest/middleware/apiKey.js +45 -0
  7. package/dist/admin/api/rest/routes/collections.d.ts +5 -0
  8. package/dist/admin/api/rest/routes/collections.js +104 -0
  9. package/dist/admin/api/rest/routes/entries.d.ts +2 -0
  10. package/dist/admin/api/rest/routes/entries.js +37 -0
  11. package/dist/admin/api/rest/routes/languages.d.ts +1 -0
  12. package/dist/admin/api/rest/routes/languages.js +5 -0
  13. package/dist/admin/api/rest/routes/schema.d.ts +2 -0
  14. package/dist/admin/api/rest/routes/schema.js +78 -0
  15. package/dist/admin/api/rest/routes/singletons.d.ts +3 -0
  16. package/dist/admin/api/rest/routes/singletons.js +60 -0
  17. package/dist/admin/auth-client.d.ts +7 -7
  18. package/dist/admin/client/collection/collection-entries.svelte +56 -5
  19. package/dist/admin/client/collection/data-table.svelte +127 -18
  20. package/dist/admin/client/collection/data-table.svelte.d.ts +2 -0
  21. package/dist/admin/client/entry/entry-form.svelte +1 -0
  22. package/dist/admin/client/entry/entry.svelte +130 -123
  23. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +92 -9
  24. package/dist/admin/components/fields/blocks-field.svelte +142 -112
  25. package/dist/admin/components/fields/blocks-field.svelte.d.ts +10 -30
  26. package/dist/admin/components/fields/boolean-field.svelte +28 -38
  27. package/dist/admin/components/fields/boolean-field.svelte.d.ts +5 -27
  28. package/dist/admin/components/fields/checkboxes-field.svelte +12 -24
  29. package/dist/admin/components/fields/checkboxes-field.svelte.d.ts +5 -27
  30. package/dist/admin/components/fields/content-field.svelte +4 -17
  31. package/dist/admin/components/fields/content-field.svelte.d.ts +5 -27
  32. package/dist/admin/components/fields/date-field.svelte +8 -21
  33. package/dist/admin/components/fields/date-field.svelte.d.ts +5 -27
  34. package/dist/admin/components/fields/datetime-field.svelte +8 -21
  35. package/dist/admin/components/fields/datetime-field.svelte.d.ts +5 -27
  36. package/dist/admin/components/fields/field-renderer.svelte +32 -19
  37. package/dist/admin/components/fields/field-renderer.svelte.d.ts +1 -1
  38. package/dist/admin/components/fields/field-value-bridge.svelte +21 -0
  39. package/dist/admin/components/fields/field-value-bridge.svelte.d.ts +31 -0
  40. package/dist/admin/components/fields/fields-form.svelte +13 -10
  41. package/dist/admin/components/fields/file-field.svelte +12 -27
  42. package/dist/admin/components/fields/file-field.svelte.d.ts +5 -27
  43. package/dist/admin/components/fields/image-field.svelte +13 -28
  44. package/dist/admin/components/fields/image-field.svelte.d.ts +5 -27
  45. package/dist/admin/components/fields/media-field.svelte +15 -30
  46. package/dist/admin/components/fields/media-field.svelte.d.ts +5 -27
  47. package/dist/admin/components/fields/number-field.svelte +6 -20
  48. package/dist/admin/components/fields/number-field.svelte.d.ts +5 -27
  49. package/dist/admin/components/fields/object-field.svelte +26 -29
  50. package/dist/admin/components/fields/object-field.svelte.d.ts +11 -31
  51. package/dist/admin/components/fields/radio-field.svelte +8 -20
  52. package/dist/admin/components/fields/radio-field.svelte.d.ts +5 -27
  53. package/dist/admin/components/fields/relation-field.svelte +28 -40
  54. package/dist/admin/components/fields/relation-field.svelte.d.ts +5 -27
  55. package/dist/admin/components/fields/richtext-field.svelte +4 -17
  56. package/dist/admin/components/fields/richtext-field.svelte.d.ts +5 -27
  57. package/dist/admin/components/fields/select-field.svelte +14 -28
  58. package/dist/admin/components/fields/select-field.svelte.d.ts +5 -27
  59. package/dist/admin/components/fields/seo-field.svelte +5 -12
  60. package/dist/admin/components/fields/seo-field.svelte.d.ts +8 -28
  61. package/dist/admin/components/fields/simple-array-field.svelte +29 -42
  62. package/dist/admin/components/fields/simple-array-field.svelte.d.ts +5 -27
  63. package/dist/admin/components/fields/slug-field.svelte +6 -11
  64. package/dist/admin/components/fields/slug-field.svelte.d.ts +6 -26
  65. package/dist/admin/components/fields/text-field-wrapper.svelte +22 -40
  66. package/dist/admin/components/fields/text-field.svelte +7 -19
  67. package/dist/admin/components/fields/text-field.svelte.d.ts +5 -27
  68. package/dist/admin/components/fields/url-field-wrapper.svelte +8 -3
  69. package/dist/admin/components/fields/url-field.svelte +294 -128
  70. package/dist/admin/components/fields/url-field.svelte.d.ts +5 -27
  71. package/dist/admin/components/layout/layout-renderer.svelte +8 -6
  72. package/dist/admin/components/layout/nav-collections.svelte +2 -1
  73. package/dist/admin/components/layout/nav-forms.svelte +2 -1
  74. package/dist/admin/components/layout/nav-singletons.svelte +2 -1
  75. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +221 -31
  76. package/dist/admin/components/tiptap/content-editor.svelte +13 -2
  77. package/dist/admin/components/tiptap/inline-block-node.d.ts +1 -0
  78. package/dist/admin/components/tiptap/inline-block-node.js +18 -1
  79. package/dist/admin/components/tiptap/slash-command.js +2 -3
  80. package/dist/admin/components/tiptap/standalone-form.d.ts +7 -0
  81. package/dist/admin/components/tiptap/standalone-form.js +31 -0
  82. package/dist/admin/components/tiptap/tiptap-editor.svelte +7 -0
  83. package/dist/admin/remote/entry.remote.d.ts +9 -1
  84. package/dist/admin/remote/entry.remote.js +30 -2
  85. package/dist/admin/styles/admin.css +10 -0
  86. package/dist/admin/utils/fieldCondition.d.ts +6 -0
  87. package/dist/admin/utils/fieldCondition.js +20 -0
  88. package/dist/cli/scaffold/admin.js +8 -0
  89. package/dist/components/ui/switch/index.d.ts +2 -0
  90. package/dist/components/ui/switch/index.js +4 -0
  91. package/dist/components/ui/switch/switch.svelte +26 -0
  92. package/dist/components/ui/switch/switch.svelte.d.ts +4 -0
  93. package/dist/core/cms.d.ts +2 -1
  94. package/dist/core/cms.js +2 -0
  95. package/dist/core/fields/fieldSchemaToTs.js +15 -3
  96. package/dist/core/fields/formFieldSchemaToTs.js +22 -6
  97. package/dist/core/fields/urlUtils.d.ts +14 -0
  98. package/dist/core/fields/urlUtils.js +21 -0
  99. package/dist/core/server/entries/operations/get.js +2 -1
  100. package/dist/core/server/entries/operations/update.d.ts +1 -0
  101. package/dist/core/server/entries/operations/update.js +5 -1
  102. package/dist/core/server/fields/populateEntry.js +43 -0
  103. package/dist/core/server/fields/resolveImageFields.js +33 -1
  104. package/dist/core/server/fields/resolveRelationFields.js +46 -0
  105. package/dist/core/server/fields/resolveRichtextLinks.js +15 -1
  106. package/dist/core/server/fields/resolveUrlFields.js +65 -0
  107. package/dist/core/server/generator/formFieldSchemaToString.js +40 -9
  108. package/dist/core/server/generator/formFields.js +2 -0
  109. package/dist/core/server/generator/generator.js +25 -1
  110. package/dist/db-postgres/schema/entry.d.ts +17 -0
  111. package/dist/db-postgres/schema/entry.js +4 -2
  112. package/dist/schemas/field/url.d.ts +2 -0
  113. package/dist/schemas/field/url.js +4 -2
  114. package/dist/server/auth.d.ts +6 -6
  115. package/dist/sveltekit/server/handle.js +1 -0
  116. package/dist/types/cms.d.ts +7 -0
  117. package/dist/types/collections.d.ts +2 -0
  118. package/dist/types/entries.d.ts +7 -1
  119. package/dist/types/fields.d.ts +9 -0
  120. package/dist/types/formFields.d.ts +15 -2
  121. package/dist/types/index.d.ts +2 -1
  122. package/dist/types/index.js +1 -0
  123. package/dist/updates/0.5.3/index.d.ts +2 -0
  124. package/dist/updates/0.5.3/index.js +19 -0
  125. package/dist/updates/0.5.4/index.d.ts +2 -0
  126. package/dist/updates/0.5.4/index.js +15 -0
  127. package/dist/updates/0.5.5/index.d.ts +2 -0
  128. package/dist/updates/0.5.5/index.js +20 -0
  129. package/dist/updates/index.js +4 -1
  130. package/package.json +7 -1
  131. package/dist/admin/components/fields/standalone-field-renderer.svelte +0 -148
  132. package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +0 -9
@@ -55,7 +55,7 @@ function generateTypesStringForForms(records) {
55
55
  .join('\n')}
56
56
  `;
57
57
  code += `
58
- export type ${recordTypeString}Map = {
58
+ export type ${recordTypeString}EntryMap = {
59
59
  ${records
60
60
  .map((single) => {
61
61
  return `${single.slug}: ${toPascalCase(single.slug)}`;
@@ -168,9 +168,33 @@ function generateSchemas(config) {
168
168
  });
169
169
  writeFileSync(filePath, code);
170
170
  }
171
+ function generateRemote(config) {
172
+ if (!config.forms || config.forms.length === 0)
173
+ return;
174
+ const cmsDir = join(process.cwd(), 'src/lib/cms/runtime');
175
+ const filePath = join(cmsDir, 'remote.ts');
176
+ let code = `// This file is auto-generated. Do not edit directly.\n\n`;
177
+ code += `import { command } from '$app/server';\n`;
178
+ code += `import { submitForm } from './api';\n`;
179
+ const schemaImports = config.forms
180
+ .map((form) => `${form.slug}FormSchema`)
181
+ .join(', ');
182
+ code += `import { ${schemaImports} } from './schemas';\n\n`;
183
+ config.forms.forEach((form) => {
184
+ const pascalSlug = toPascalCase(form.slug);
185
+ code += `export const submit${pascalSlug}Command = command(\n`;
186
+ code += `\t${form.slug}FormSchema,\n`;
187
+ code += `\tasync (data) => {\n`;
188
+ code += `\t\tawait submitForm('${form.slug}', data);\n`;
189
+ code += `\t}\n`;
190
+ code += `);\n\n`;
191
+ });
192
+ writeFileSync(filePath, code);
193
+ }
171
194
  export function generateRuntime(config) {
172
195
  createCmsRuntimeDir();
173
196
  generateTypes(config);
174
197
  generateAPI(config);
175
198
  generateSchemas(config);
199
+ generateRemote(config);
176
200
  }
@@ -200,6 +200,23 @@ export declare const entriesTable: import("drizzle-orm/pg-core/table", { with: {
200
200
  identity: undefined;
201
201
  generated: undefined;
202
202
  }, {}, {}>;
203
+ sortOrder: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
204
+ name: "sort_order";
205
+ tableName: "entry";
206
+ dataType: "number";
207
+ columnType: "PgInteger";
208
+ data: number;
209
+ driverParam: string | number;
210
+ notNull: false;
211
+ hasDefault: false;
212
+ isPrimaryKey: false;
213
+ isAutoincrement: false;
214
+ hasRuntimeDefault: false;
215
+ enumValues: undefined;
216
+ baseColumn: never;
217
+ identity: undefined;
218
+ generated: undefined;
219
+ }, {}, {}>;
203
220
  };
204
221
  dialect: "pg";
205
222
  }>;
@@ -1,4 +1,4 @@
1
- import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
1
+ import { integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
2
2
  import { entryVersionsTable } from './entryVersion.js';
3
3
  export const entriesTable = pgTable('entry', {
4
4
  id: uuid('id').primaryKey().defaultRandom(),
@@ -12,5 +12,7 @@ export const entriesTable = pgTable('entry', {
12
12
  // Publish state
13
13
  publishedAt: timestamp('published_at'),
14
14
  publishedVersionId: uuid('published_version_id').references(() => entryVersionsTable.id, { onDelete: 'set null' }),
15
- publishedBy: text('published_by')
15
+ publishedBy: text('published_by'),
16
+ // Manual ordering
17
+ sortOrder: integer('sort_order')
16
18
  });
@@ -4,10 +4,12 @@ export declare const urlFieldDataSchema: z.ZodObject<{
4
4
  url: z.ZodRecord<z.ZodString, z.ZodString>;
5
5
  text: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
6
6
  newTab: z.ZodOptional<z.ZodBoolean>;
7
+ rel: z.ZodOptional<z.ZodString>;
7
8
  }, z.z.core.$strip>;
8
9
  export declare const urlFieldDataWithRelationSchema: z.ZodObject<{
9
10
  id: z.ZodString;
10
11
  url: z.ZodRecord<z.ZodString, z.ZodString>;
11
12
  text: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
12
13
  newTab: z.ZodOptional<z.ZodBoolean>;
14
+ rel: z.ZodOptional<z.ZodString>;
13
15
  }, z.z.core.$strip>;
@@ -3,11 +3,13 @@ export const urlFieldDataSchema = z.object({
3
3
  id: z.string().optional(),
4
4
  url: z.record(z.string(), z.string()),
5
5
  text: z.record(z.string(), z.string()).optional(),
6
- newTab: z.boolean().optional()
6
+ newTab: z.boolean().optional(),
7
+ rel: z.string().optional()
7
8
  });
8
9
  export const urlFieldDataWithRelationSchema = z.object({
9
10
  id: z.string().uuid(),
10
11
  url: z.record(z.string(), z.string()),
11
12
  text: z.record(z.string(), z.string()).optional(),
12
- newTab: z.boolean().optional()
13
+ newTab: z.boolean().optional(),
14
+ rel: z.string().optional()
13
15
  });
@@ -71,7 +71,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
71
71
  <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0: {
72
72
  body: {
73
73
  userId: string;
74
- role: "user" | "admin" | ("user" | "admin")[];
74
+ role: "admin" | "user" | ("admin" | "user")[];
75
75
  };
76
76
  } & {
77
77
  method?: "POST" | undefined;
@@ -138,7 +138,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
138
138
  $Infer: {
139
139
  body: {
140
140
  userId: string;
141
- role: "user" | "admin" | ("user" | "admin")[];
141
+ role: "admin" | "user" | ("admin" | "user")[];
142
142
  };
143
143
  };
144
144
  };
@@ -236,7 +236,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
236
236
  email: string;
237
237
  password: string;
238
238
  name: string;
239
- role?: "user" | "admin" | ("user" | "admin")[] | undefined;
239
+ role?: "admin" | "user" | ("admin" | "user")[] | undefined;
240
240
  data?: Record<string, any>;
241
241
  };
242
242
  } & {
@@ -302,7 +302,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
302
302
  email: string;
303
303
  password: string;
304
304
  name: string;
305
- role?: "user" | "admin" | ("user" | "admin")[] | undefined;
305
+ role?: "admin" | "user" | ("admin" | "user")[] | undefined;
306
306
  data?: Record<string, any>;
307
307
  };
308
308
  };
@@ -1184,7 +1184,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
1184
1184
  permission?: never;
1185
1185
  }) & {
1186
1186
  userId?: string;
1187
- role?: "user" | "admin" | undefined;
1187
+ role?: "admin" | "user" | undefined;
1188
1188
  };
1189
1189
  } & {
1190
1190
  method?: "POST" | undefined;
@@ -1287,7 +1287,7 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
1287
1287
  permission?: never;
1288
1288
  }) & {
1289
1289
  userId?: string;
1290
- role?: "user" | "admin" | undefined;
1290
+ role?: "admin" | "user" | undefined;
1291
1291
  };
1292
1292
  };
1293
1293
  };
@@ -11,6 +11,7 @@ const adminGuard = async ({ event, resolve }) => {
11
11
  !event.url.pathname.startsWith('/admin/accept-invite') &&
12
12
  !event.url.pathname.startsWith('/admin/api/accept-invite') &&
13
13
  !event.url.pathname.startsWith('/admin/reset-password') &&
14
+ !event.url.pathname.startsWith('/admin/api/rest/') &&
14
15
  (!user || !session)) {
15
16
  setFlash({
16
17
  message: 'You must be logged in to access the admin panel.',
@@ -27,6 +27,11 @@ export interface AuthAdapter {
27
27
  } | null>;
28
28
  };
29
29
  }
30
+ export interface ApiKeyConfig {
31
+ key: string;
32
+ name?: string;
33
+ role?: 'admin' | 'editor';
34
+ }
30
35
  export interface CMSConfig {
31
36
  languages: Language[];
32
37
  collections?: CollectionConfig[];
@@ -39,6 +44,7 @@ export interface CMSConfig {
39
44
  plugins?: PluginConfig[];
40
45
  ai?: AIAdapter;
41
46
  media?: MediaConfig;
47
+ apiKeys?: ApiKeyConfig[];
42
48
  }
43
49
  export interface ICMS {
44
50
  collections: Record<string, CollectionConfigWithType>;
@@ -52,4 +58,5 @@ export interface ICMS {
52
58
  plugins: PluginConfig[];
53
59
  aiAdapter: AIAdapter | null;
54
60
  mediaConfig: MediaConfig;
61
+ apiKeys: ApiKeyConfig[];
55
62
  }
@@ -6,6 +6,8 @@ export interface CollectionConfig extends ConfigBase {
6
6
  singular?: Localized;
7
7
  plural?: Localized;
8
8
  };
9
+ orderable?: boolean;
10
+ listColumns?: string[];
9
11
  }
10
12
  export interface CollectionConfigWithType extends CollectionConfig {
11
13
  type: 'collection';
@@ -19,6 +19,7 @@ export interface DbEntry {
19
19
  publishedAt: Date | null;
20
20
  publishedVersionId: string | null;
21
21
  publishedBy: string | null;
22
+ sortOrder: number | null;
22
23
  }
23
24
  export interface DbEntryInsert {
24
25
  slug: string;
@@ -30,6 +31,7 @@ export interface DbEntryInsert {
30
31
  publishedAt?: Date | null;
31
32
  publishedVersionId?: string | null;
32
33
  publishedBy?: string | null;
34
+ sortOrder?: number | null;
33
35
  }
34
36
  export interface RawEntry extends DbEntry {
35
37
  collection: CollectionConfigWithType | SingleConfigWithType;
@@ -68,7 +70,7 @@ export interface PaginationOptions {
68
70
  limit?: number;
69
71
  offset?: number;
70
72
  orderBy?: {
71
- column: 'createdAt' | 'updatedAt';
73
+ column: 'createdAt' | 'updatedAt' | 'sortOrder';
72
74
  direction: 'asc' | 'desc';
73
75
  };
74
76
  }
@@ -103,6 +105,10 @@ export interface GetEntriesOptions {
103
105
  dataLike?: Record<string, unknown>;
104
106
  language?: string;
105
107
  status?: EntryStatus;
108
+ orderBy?: {
109
+ column: 'createdAt' | 'updatedAt' | 'sortOrder';
110
+ direction: 'asc' | 'desc';
111
+ };
106
112
  }
107
113
  export interface GetEntryOptions {
108
114
  id?: string;
@@ -3,6 +3,11 @@ import type { ImageStyle, MediaFile } from './media.js';
3
3
  import type { Localized } from './languages.js';
4
4
  import type { StructuredContentDoc } from './structured-content.js';
5
5
  export type FieldType = 'text' | 'richtext' | 'content' | 'number' | 'boolean' | 'date' | 'datetime' | 'file' | 'image' | 'media' | 'select' | 'radio' | 'checkboxes' | 'relation' | 'object' | 'array' | 'blocks' | 'slug' | 'seo' | 'url';
6
+ export interface FieldCondition {
7
+ field: string;
8
+ equals?: string | string[];
9
+ notEquals?: string | string[];
10
+ }
6
11
  export interface BaseField {
7
12
  slug: string;
8
13
  label?: Localized;
@@ -10,6 +15,7 @@ export interface BaseField {
10
15
  description?: Localized;
11
16
  defaultValue?: any;
12
17
  localized?: boolean;
18
+ showWhen?: FieldCondition;
13
19
  }
14
20
  export interface TextField extends BaseField {
15
21
  type: 'text';
@@ -144,6 +150,7 @@ export interface ObjectField extends BaseField {
144
150
  thumbnail?: string;
145
151
  }
146
152
  export interface ObjectFieldData {
153
+ _id?: string;
147
154
  slug?: string;
148
155
  data: Record<string, unknown>;
149
156
  }
@@ -188,11 +195,13 @@ export interface UrlField extends BaseField {
188
195
  placeholder?: Localized;
189
196
  text?: boolean;
190
197
  newTab?: boolean;
198
+ rel?: boolean;
191
199
  }
192
200
  export type UrlFieldData = {
193
201
  id?: string;
194
202
  url: Record<string, string>;
195
203
  text?: Record<string, string>;
196
204
  newTab?: boolean;
205
+ rel?: string;
197
206
  };
198
207
  export type Field = TextField | RichtextField | ContentField | NumberField | BooleanField | DateField | DateTimeField | FileField | ImageField | MediaField | SelectField | RadioField | CheckboxesField | RelationField | ObjectField | ArrayField | BlocksField | SlugField | SeoField | UrlField;
@@ -1,16 +1,19 @@
1
1
  import type { Localized } from './languages.js';
2
- export type FormFieldType = 'text' | 'email' | 'textarea' | 'checkbox';
2
+ export type FormFieldType = 'text' | 'email' | 'textarea' | 'checkbox' | 'select';
3
3
  export interface FormBaseField {
4
4
  slug: string;
5
5
  label?: Localized;
6
6
  required?: boolean;
7
7
  description?: Localized;
8
+ errorMessage?: Localized;
8
9
  defaultValue?: any;
9
10
  showInDataTable?: boolean;
10
11
  }
11
12
  export interface FormTextField extends FormBaseField {
12
13
  type: 'text';
13
14
  defaultValue?: string;
15
+ minLength?: number;
16
+ maxLength?: number;
14
17
  }
15
18
  export interface FormEmailField extends FormBaseField {
16
19
  type: 'email';
@@ -19,9 +22,19 @@ export interface FormEmailField extends FormBaseField {
19
22
  export interface FormTextareaField extends FormBaseField {
20
23
  type: 'textarea';
21
24
  defaultValue?: string;
25
+ minLength?: number;
26
+ maxLength?: number;
22
27
  }
23
28
  export interface FormCheckboxField extends FormBaseField {
24
29
  type: 'checkbox';
25
30
  defaultValue?: boolean;
26
31
  }
27
- export type FormField = FormTextField | FormEmailField | FormTextareaField | FormCheckboxField;
32
+ export interface FormSelectField extends FormBaseField {
33
+ type: 'select';
34
+ options: {
35
+ value: string;
36
+ label?: Localized;
37
+ }[];
38
+ defaultValue?: string;
39
+ }
40
+ export type FormField = FormTextField | FormEmailField | FormTextareaField | FormCheckboxField | FormSelectField;
@@ -7,6 +7,7 @@ export { type ConfigBase } from './config.js';
7
7
  export { type CollectionConfig } from './collections.js';
8
8
  export { type SingleConfig } from './singles.js';
9
9
  export { type FormConfig, type FormSubmission } from './forms.js';
10
- export { type CMSConfig } from './cms.js';
10
+ export { type FormField, type FormFieldType, type FormBaseField, type FormTextField, type FormEmailField, type FormTextareaField, type FormCheckboxField, type FormSelectField } from './formFields.js';
11
+ export { type CMSConfig, type ApiKeyConfig } from './cms.js';
11
12
  export { type Language, type Localized } from './languages.js';
12
13
  export { type Layout, type LayoutNode, type LayoutPreset, type LayoutNodeType, type ColumnRatio, type SectionNode, type ColumnsNode, type CardNode, type AccordionNode, type StackNode } from './layout.js';
@@ -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';
@@ -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
+ };
@@ -0,0 +1,2 @@
1
+ import type { CmsUpdate } from '../index.js';
2
+ export declare const update: CmsUpdate;
@@ -0,0 +1,15 @@
1
+ export const update = {
2
+ version: '0.5.4',
3
+ date: '2026-03-04',
4
+ description: 'Collection ordering (DnD), custom list columns',
5
+ features: [
6
+ 'Orderable collections: DnD reordering of entries in admin list with keyboard alternative (WCAG 2.1)',
7
+ 'Custom list columns: display arbitrary entry fields as columns in collection list',
8
+ 'sortOrder column on entries table for persistent manual ordering',
9
+ 'reorderEntriesCommand remote for bulk sort order updates'
10
+ ],
11
+ fixes: [],
12
+ breakingChanges: [],
13
+ sql: 'ALTER TABLE entry ADD COLUMN sort_order INTEGER;',
14
+ notes: 'Add `orderable: true` to collections that need manual ordering. Add `listColumns: ["fieldSlug"]` to show fields in the entry list.'
15
+ };
@@ -0,0 +1,2 @@
1
+ import type { CmsUpdate } from '../index.js';
2
+ export declare const update: CmsUpdate;
@@ -0,0 +1,20 @@
1
+ export const update = {
2
+ version: '0.5.5',
3
+ date: '2026-03-04',
4
+ description: 'REST API with API key auth, orderable collection fixes',
5
+ features: [
6
+ 'REST API with API key authentication — CRUD for collections, singletons, entries, schema introspection, language listing',
7
+ 'API key configuration in CMS config (`apiKeys` array)',
8
+ 'getEntries supports `orderBy` parameter (column + direction)',
9
+ 'Admin scaffold generates REST API catch-all route'
10
+ ],
11
+ fixes: [
12
+ 'Orderable collections: DnD rows use proper `<tr>` element instead of `<div>` wrapper',
13
+ 'Reorder buttons always visible (not sr-only) for better discoverability',
14
+ 'Selection enabled for orderable collections',
15
+ 'Relation field respects orderable collection sort order',
16
+ 'Nav active state: exact match prevents false highlights on similarly-named routes'
17
+ ],
18
+ breakingChanges: [],
19
+ notes: 'Add `apiKeys: [{ key: "your-secret", name: "My App" }]` to CMS config. REST API available at `/admin/api/rest/`.'
20
+ };
@@ -14,7 +14,10 @@ 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
16
  import { update as update052 } from './0.5.2/index.js';
17
- export const updates = [update0065, update0066, update0067, update0068, update0069, update010, update011, update012, update013, update014, update015, update020, update022, update050, update051, update052];
17
+ import { update as update053 } from './0.5.3/index.js';
18
+ import { update as update054 } from './0.5.4/index.js';
19
+ import { update as update055 } from './0.5.5/index.js';
20
+ export const updates = [update0065, update0066, update0067, update0068, update0069, update010, update011, update012, update013, update014, update015, update020, update022, update050, update051, update052, update053, update054, update055];
18
21
  export const getUpdatesFrom = (fromVersion) => {
19
22
  const fromParts = fromVersion.split('.').map(Number);
20
23
  return updates.filter((update) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "includio-cms",
3
- "version": "0.5.2",
3
+ "version": "0.5.5",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -63,6 +63,11 @@
63
63
  "svelte": "./dist/admin/api/*.js",
64
64
  "node": "./dist/admin/api/*.js"
65
65
  },
66
+ "./admin/api/rest/handler": {
67
+ "types": "./dist/admin/api/rest/handler.d.ts",
68
+ "svelte": "./dist/admin/api/rest/handler.js",
69
+ "node": "./dist/admin/api/rest/handler.js"
70
+ },
66
71
  "./admin/client/account": {
67
72
  "types": "./dist/admin/client/account/index.d.ts",
68
73
  "svelte": "./dist/admin/client/account/index.js"
@@ -199,6 +204,7 @@
199
204
  "@tiptap/extension-highlight": "^3.17.1",
200
205
  "@tiptap/extension-image": "^3.17.1",
201
206
  "@tiptap/extension-link": "^3.17.1",
207
+ "@tiptap/extension-placeholder": "^3.20.0",
202
208
  "@tiptap/extension-table": "^3.17.1",
203
209
  "@tiptap/extension-table-cell": "^3.17.1",
204
210
  "@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;