includio-cms 0.14.4 → 0.14.6
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.
- package/CHANGELOG.md +30 -0
- package/DOCS.md +1 -1
- package/ROADMAP.md +11 -0
- package/dist/admin/client/account/profile-section.svelte +2 -2
- package/dist/admin/client/admin/admin-after-login-layout-content.svelte +12 -1
- package/dist/admin/client/collection/collection-entries.svelte +48 -4
- package/dist/admin/client/login/login-page.svelte +4 -0
- package/dist/admin/client/login/reset-password-page.svelte +4 -0
- package/dist/admin/client/maintenance/maintenance-page.svelte +12 -0
- package/dist/admin/client/users/accept-invite-page.svelte +4 -0
- package/dist/admin/client/users/users-page.svelte +10 -0
- package/dist/admin/components/fields/media-field.svelte +203 -253
- package/dist/admin/components/fields/relation-field.svelte +4 -9
- package/dist/admin/components/fields/url-field.svelte +42 -18
- package/dist/admin/components/layout/nav-footer.svelte +12 -9
- package/dist/admin/components/media/file/file-details.svelte +115 -18
- package/dist/admin/components/media/file/file-miniature.svelte +2 -2
- package/dist/admin/components/media/media-selector.svelte +9 -8
- package/dist/admin/remote/media.remote.d.ts +6 -4
- package/dist/admin/remote/media.remote.js +17 -1
- package/dist/admin/utils/debounce.d.ts +1 -0
- package/dist/admin/utils/debounce.js +8 -0
- package/dist/admin/utils/entryThumbnail.d.ts +6 -1
- package/dist/admin/utils/entryThumbnail.js +22 -13
- package/dist/admin/utils/pageTitle.d.ts +8 -0
- package/dist/admin/utils/pageTitle.js +15 -0
- package/dist/cms/runtime/types.d.ts +4 -4
- package/dist/core/cms.d.ts +1 -0
- package/dist/core/cms.js +2 -0
- package/dist/core/server/media/operations/batchRegenerateVideoPosters.d.ts +12 -0
- package/dist/core/server/media/operations/batchRegenerateVideoPosters.js +46 -42
- package/dist/core/server/media/operations/regenerateVideoPoster.d.ts +5 -0
- package/dist/core/server/media/operations/regenerateVideoPoster.js +17 -0
- package/dist/core/server/media/operations/replaceFile.js +8 -4
- package/dist/core/server/media/operations/uploadFile.js +7 -3
- package/dist/core/server/media/styles/operations/batchGenerateStyles.js +11 -0
- package/dist/core/server/media/utils/generateAdminThumbnail.d.ts +3 -0
- package/dist/core/server/media/utils/generateAdminThumbnail.js +33 -0
- package/dist/db-postgres/schema/imageStyle.js +5 -4
- package/dist/db-postgres/schema/videoStyle.js +2 -4
- package/dist/files-local/video.js +3 -18
- package/dist/types/cms.d.ts +1 -0
- package/dist/updates/0.14.5/index.d.ts +2 -0
- package/dist/updates/0.14.5/index.js +11 -0
- package/dist/updates/0.14.6/index.d.ts +2 -0
- package/dist/updates/0.14.6/index.js +21 -0
- package/dist/updates/index.js +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,36 @@
|
|
|
3
3
|
All notable changes to includio-cms are documented here.
|
|
4
4
|
Generated from `src/lib/updates/` — do not edit manually.
|
|
5
5
|
|
|
6
|
+
## 0.14.6 — 2026-04-13
|
|
7
|
+
|
|
8
|
+
Admin page titles — meaningful <title> in every admin route
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- CMS config: new optional `sidebarHelp` flag (default `true`) — set to `false` to hide the "Help" link in the admin sidebar footer
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Video posters are now always generated from the first frame — eliminates the visible "jump" when the player starts at 0s after showing a poster taken from a later frame. Existing posters can be rebuilt via Maintenance → "Regenerate video posters"
|
|
15
|
+
- Media library → file details: new "Regenerate from video" button for video files to rebuild the poster/thumbnail on demand (e.g. after replacing a file or when the automatic poster looked wrong)
|
|
16
|
+
- Admin routes now render a descriptive <title> derived from the breadcrumbs trail (e.g. "Entry label · Collection · AriaCMS") instead of showing the raw URL in the browser tab
|
|
17
|
+
- Pre-login routes (login, password reset, accept invite) set their own titles
|
|
18
|
+
- Brand "AriaCMS" used across admin titles (replaces "Includio CMS")
|
|
19
|
+
- Media field: video preview now renders in the same aspect-square box as image preview (checkered background, object-contain) instead of a tall full-width player
|
|
20
|
+
- Media field: unified layout — Change/Remove buttons sit below the preview box for both images and videos (no more overlay covering the image)
|
|
21
|
+
- Media field: video previews now show the file-type icon in the top-right corner (parity with image preview)
|
|
22
|
+
- Media field: accessibility status is now surfaced as a compact informational badge in the top-left corner of the video preview with a tooltip describing what is missing (transcript, audio description); transcript/audio-description management was moved to the file details panel in the media library (single source of truth per file)
|
|
23
|
+
- Relation field: fixed missing space between "Select" and collection label in the picker trigger (e.g. "Select Price category" instead of "SelectPrice category")
|
|
24
|
+
|
|
25
|
+
## 0.14.5 — 2026-04-10
|
|
26
|
+
|
|
27
|
+
Schema: unique indexes moved into Drizzle ORM definitions
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- Image & video style unique indexes now defined in Drizzle schema (single source of truth, no more SQL-only index comments)
|
|
31
|
+
|
|
32
|
+
### Notes
|
|
33
|
+
|
|
34
|
+
No SQL migration needed — indexes already exist in DB. This only moves definitions from SQL comments into Drizzle ORM schema.
|
|
35
|
+
|
|
6
36
|
## 0.14.4 — 2026-03-31
|
|
7
37
|
|
|
8
38
|
Improved type generation — select/checkboxes emit union types, fields with defaultValue are non-optional
|
package/DOCS.md
CHANGED
package/ROADMAP.md
CHANGED
|
@@ -276,6 +276,17 @@
|
|
|
276
276
|
- [x] `[feature]` `[P2]` Configurable maintenance interval via `media.maintenance` config <!-- files: src/lib/types/cms.ts -->
|
|
277
277
|
- [x] `[feature]` `[P1]` Maintenance page UI redesign — status banner, sections, SSE handler dedup <!-- files: src/lib/admin/client/maintenance/maintenance-page.svelte -->
|
|
278
278
|
|
|
279
|
+
## 0.14.5 — Schema cleanup
|
|
280
|
+
|
|
281
|
+
- [x] `[chore]` `[P2]` Move image & video style unique indexes into Drizzle ORM schema (single source of truth) <!-- files: src/lib/db-postgres/schema/imageStyle.ts, src/lib/db-postgres/schema/videoStyle.ts -->
|
|
282
|
+
|
|
283
|
+
## 0.14.6 — Admin page titles
|
|
284
|
+
|
|
285
|
+
- [x] `[fix]` `[P2]` Admin `<title>` from breadcrumbs + pre-login titles + AriaCMS brand <!-- files: src/lib/admin/utils/pageTitle.ts, src/lib/admin/client/admin/admin-after-login-layout-content.svelte -->
|
|
286
|
+
- [x] `[fix]` `[P1]` Video poster always from first frame (no jump on load) <!-- files: src/lib/files-local/video.ts -->
|
|
287
|
+
- [x] `[feature]` `[P2]` Manual poster regeneration in media library file details <!-- files: src/lib/core/server/media/operations/regenerateVideoPoster.ts, src/lib/admin/components/media/file/file-details.svelte -->
|
|
288
|
+
- [x] `[feature]` `[P2]` Config flag `sidebarHelp` to hide admin sidebar Help link (default true) <!-- files: src/lib/types/cms.ts, src/lib/core/cms.ts, src/routes/admin/(afterLogin)/+layout.server.ts, src/lib/admin/components/layout/nav-footer.svelte -->
|
|
289
|
+
|
|
279
290
|
## 0.15.0 — SEO module
|
|
280
291
|
|
|
281
292
|
- [ ] `[feature]` `[P1]` SERP preview + character limits for title/description <!-- files: src/lib/admin/components/fields/seo-field.svelte -->
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
|
|
89
89
|
<!-- Email readonly -->
|
|
90
90
|
<div class="flex flex-col gap-1">
|
|
91
|
-
|
|
91
|
+
<!-- svelte-ignore a11y_label_has_associated_control -->
|
|
92
92
|
<label class="text-sm font-medium">{lang.profile.email}</label>
|
|
93
93
|
<div class="acct-input-wrap">
|
|
94
94
|
<Input
|
|
@@ -119,7 +119,7 @@ tttttt<!-- svelte-ignore a11y_label_has_associated_control -->
|
|
|
119
119
|
|
|
120
120
|
<!-- Role badge -->
|
|
121
121
|
<div class="flex flex-col gap-1">
|
|
122
|
-
|
|
122
|
+
<!-- svelte-ignore a11y_label_has_associated_control -->
|
|
123
123
|
<label class="text-sm font-medium">{lang.profile.role}</label>
|
|
124
124
|
<div>
|
|
125
125
|
<span
|
|
@@ -5,13 +5,24 @@
|
|
|
5
5
|
import SiteHeader from '../../components/layout/site-header.svelte';
|
|
6
6
|
import { Breadcrumbs, setBreadcrumbs } from '../../state/breadcrumbs.svelte.js';
|
|
7
7
|
import { ContentLanguage, setContentLanguage } from '../../state/content-language.svelte.js';
|
|
8
|
+
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
9
|
+
import { formatTitle } from '../../utils/pageTitle.js';
|
|
8
10
|
|
|
9
11
|
let { languages, children }: { languages: string[]; children: Snippet } = $props();
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
const breadcrumbs = new Breadcrumbs();
|
|
14
|
+
setBreadcrumbs(breadcrumbs);
|
|
12
15
|
setContentLanguage(new ContentLanguage(languages, languages[0]));
|
|
16
|
+
|
|
17
|
+
const interfaceLanguage = useInterfaceLanguage();
|
|
18
|
+
const fallback = $derived(interfaceLanguage.current === 'pl' ? 'Panel' : 'Admin');
|
|
19
|
+
const title = $derived(formatTitle(breadcrumbs.state, fallback));
|
|
13
20
|
</script>
|
|
14
21
|
|
|
22
|
+
<svelte:head>
|
|
23
|
+
<title>{title}</title>
|
|
24
|
+
</svelte:head>
|
|
25
|
+
|
|
15
26
|
<Sidebar.Provider style="--sidebar-width: 16rem; --header-height: calc(var(--spacing) * 13);">
|
|
16
27
|
<AppSidebar />
|
|
17
28
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getRawCollectionEntryLabel } from '../../utils/entryLabel.js';
|
|
3
|
-
import { getEntryThumbnail } from '../../utils/entryThumbnail.js';
|
|
3
|
+
import { getEntryThumbnail, type MediaThumbnailLookup } from '../../utils/entryThumbnail.js';
|
|
4
4
|
import { arrayMove } from '../../utils/arrayMove.js';
|
|
5
5
|
import { getRemotes } from '../../../sveltekit/index.js';
|
|
6
6
|
import type { CollectionConfigWithType } from '../../../types/collections.js';
|
|
@@ -246,11 +246,17 @@
|
|
|
246
246
|
// Track relation labels
|
|
247
247
|
let labelLookup = $state<Record<string, string>>({});
|
|
248
248
|
|
|
249
|
+
// Track media thumbnails for grid/list view
|
|
250
|
+
let mediaLookup = $state<MediaThumbnailLookup>({});
|
|
251
|
+
|
|
249
252
|
$effect(() => {
|
|
250
253
|
if (entriesQuery.current) {
|
|
251
254
|
fetchRelationLabels(entriesQuery.current.entries).then((l) => {
|
|
252
255
|
labelLookup = l;
|
|
253
256
|
}).catch(() => {});
|
|
257
|
+
fetchMediaThumbnails(entriesQuery.current.entries).then((m) => {
|
|
258
|
+
mediaLookup = m;
|
|
259
|
+
}).catch(() => {});
|
|
254
260
|
}
|
|
255
261
|
});
|
|
256
262
|
|
|
@@ -498,7 +504,45 @@
|
|
|
498
504
|
return lookup;
|
|
499
505
|
}
|
|
500
506
|
|
|
501
|
-
function
|
|
507
|
+
async function fetchMediaThumbnails(entries: RawEntry[]): Promise<MediaThumbnailLookup> {
|
|
508
|
+
const ids = new Set<string>();
|
|
509
|
+
for (const entry of entries) {
|
|
510
|
+
const data = getVersionData(entry);
|
|
511
|
+
if (!data) continue;
|
|
512
|
+
collectMediaUuids(data, ids);
|
|
513
|
+
}
|
|
514
|
+
if (ids.size === 0) return {};
|
|
515
|
+
|
|
516
|
+
const files = await remotes.getMediaFiles({
|
|
517
|
+
data: { ids: Array.from(ids) }
|
|
518
|
+
});
|
|
519
|
+
const lookup: MediaThumbnailLookup = {};
|
|
520
|
+
for (const file of files) {
|
|
521
|
+
if (file.type === 'image') {
|
|
522
|
+
lookup[file.id] = file.thumbnailUrl ?? file.url;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return lookup;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function collectMediaUuids(obj: unknown, ids: Set<string>, depth = 0): void {
|
|
529
|
+
if (depth > 5 || obj === null || obj === undefined) return;
|
|
530
|
+
if (typeof obj === 'string' && UUID_RE.test(obj)) {
|
|
531
|
+
ids.add(obj);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (Array.isArray(obj)) {
|
|
535
|
+
for (const item of obj) collectMediaUuids(item, ids, depth + 1);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (typeof obj === 'object') {
|
|
539
|
+
for (const val of Object.values(obj as Record<string, unknown>)) {
|
|
540
|
+
collectMediaUuids(val, ids, depth + 1);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function mapEntryToRow(entry: RawEntry, lookup: Record<string, string> = {}, mediaThumbs: MediaThumbnailLookup = {}): CollectionDataTableRow {
|
|
502
546
|
const data = getVersionData(entry) || {};
|
|
503
547
|
// For custom columns, prefer published data (complete) over draft (may be partial)
|
|
504
548
|
const lang = contentLanguage.current;
|
|
@@ -543,7 +587,7 @@
|
|
|
543
587
|
status: getEntryStatus(entry),
|
|
544
588
|
createdAt: new Date(entry.createdAt),
|
|
545
589
|
updatedAt: new Date(entry.updatedAt),
|
|
546
|
-
thumbnail: getEntryThumbnail(data as Record<string, unknown>, collection),
|
|
590
|
+
thumbnail: getEntryThumbnail(data as Record<string, unknown>, collection, mediaThumbs),
|
|
547
591
|
searchText: getSearchText(entry),
|
|
548
592
|
a11yWarnings: countA11yWarnings(entry),
|
|
549
593
|
customData
|
|
@@ -658,7 +702,7 @@
|
|
|
658
702
|
</div>
|
|
659
703
|
{:else if entriesQuery.current}
|
|
660
704
|
{@const result = entriesQuery.current}
|
|
661
|
-
{@const allRows = result.entries.map((e) => mapEntryToRow(e, labelLookup))}
|
|
705
|
+
{@const allRows = result.entries.map((e) => mapEntryToRow(e, labelLookup, mediaLookup))}
|
|
662
706
|
{@const filteredRows = (() => {
|
|
663
707
|
let rows = allRows;
|
|
664
708
|
// Client-side status filter (non-archived statuses)
|
|
@@ -17,6 +17,18 @@
|
|
|
17
17
|
import Button from '../../../components/ui/button/button.svelte';
|
|
18
18
|
import * as Card from '../../../components/ui/card/index.js';
|
|
19
19
|
import { toast } from 'svelte-sonner';
|
|
20
|
+
import { getBreadcrumbs } from '../../state/breadcrumbs.svelte.js';
|
|
21
|
+
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
22
|
+
import { sidebarLang } from '../../components/layout/lang.js';
|
|
23
|
+
|
|
24
|
+
const breadcrumbs = getBreadcrumbs();
|
|
25
|
+
const interfaceLanguage = useInterfaceLanguage();
|
|
26
|
+
|
|
27
|
+
$effect(() => {
|
|
28
|
+
breadcrumbs.state = [
|
|
29
|
+
{ label: sidebarLang[interfaceLanguage.current].main.maintenance }
|
|
30
|
+
];
|
|
31
|
+
});
|
|
20
32
|
|
|
21
33
|
interface GcReport {
|
|
22
34
|
imageStylesCount: number;
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
import * as Table from '../../../components/ui/table/index.js';
|
|
22
22
|
import { authClient } from '../../auth-client.js';
|
|
23
23
|
import { usersLang } from './lang.js';
|
|
24
|
+
import { sidebarLang } from '../../components/layout/lang.js';
|
|
25
|
+
import { getBreadcrumbs } from '../../state/breadcrumbs.svelte.js';
|
|
24
26
|
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
25
27
|
import { toLocaleCode } from '../../utils/formatDate.js';
|
|
26
28
|
import { getRoleLabel } from '../../utils/roleLabel.js';
|
|
@@ -48,6 +50,14 @@
|
|
|
48
50
|
|
|
49
51
|
const interfaceLanguage = useInterfaceLanguage();
|
|
50
52
|
const lang = $derived(usersLang[interfaceLanguage.current]);
|
|
53
|
+
const breadcrumbs = getBreadcrumbs();
|
|
54
|
+
|
|
55
|
+
$effect(() => {
|
|
56
|
+
breadcrumbs.state = [
|
|
57
|
+
{ label: sidebarLang[interfaceLanguage.current].main.users }
|
|
58
|
+
];
|
|
59
|
+
});
|
|
60
|
+
|
|
51
61
|
const session = authClient.useSession();
|
|
52
62
|
const currentUserId = $derived(session.value?.data?.user?.id ?? '');
|
|
53
63
|
|