@questpie/admin 3.5.3 → 3.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 (141) hide show
  1. package/README.md +8 -0
  2. package/dist/client/blocks/block-renderer.d.mts +2 -2
  3. package/dist/client/builder/index.d.mts +1 -1
  4. package/dist/client/builder/types/collection-types.d.mts +80 -5
  5. package/dist/client/builder/types/common.d.mts +5 -0
  6. package/dist/client/builder/types/field-types.d.mts +41 -1
  7. package/dist/client/builder/view/view.d.mts +3 -2
  8. package/dist/client/components/admin-link.d.mts +2 -2
  9. package/dist/client/components/fields/boolean-field.mjs +2 -1
  10. package/dist/client/components/fields/date-field.mjs +2 -1
  11. package/dist/client/components/fields/datetime-field.mjs +2 -1
  12. package/dist/client/components/fields/email-field.mjs +2 -1
  13. package/dist/client/components/fields/field-utils.d.mts +11 -0
  14. package/dist/client/components/fields/field-utils.mjs +3 -1
  15. package/dist/client/components/fields/field-wrapper.mjs +3 -3
  16. package/dist/client/components/fields/number-field.mjs +2 -1
  17. package/dist/client/components/fields/object-field.mjs +2 -1
  18. package/dist/client/components/fields/relation/displays/types.mjs +3 -3
  19. package/dist/client/components/fields/rich-text-editor/extensions.mjs +2 -1
  20. package/dist/client/components/fields/rich-text-editor/image-popover.mjs +6 -2
  21. package/dist/client/components/fields/rich-text-editor/image-upload.mjs +2 -1
  22. package/dist/client/components/fields/rich-text-editor/index.d.mts +3 -2
  23. package/dist/client/components/fields/rich-text-editor/index.mjs +4 -3
  24. package/dist/client/components/fields/select-field.mjs +2 -1
  25. package/dist/client/components/fields/text-field.mjs +2 -1
  26. package/dist/client/components/fields/textarea-field.mjs +2 -1
  27. package/dist/client/components/fields/time-field.mjs +2 -1
  28. package/dist/client/components/layout/field-layout-renderer.mjs +4 -4
  29. package/dist/client/components/media/media-grid.mjs +2 -1
  30. package/dist/client/components/primitives/asset-preview.mjs +4 -2
  31. package/dist/client/components/primitives/dropzone.d.mts +100 -0
  32. package/dist/client/components/primitives/field-select-control.mjs +2 -1
  33. package/dist/client/components/ui/button.d.mts +23 -0
  34. package/dist/client/components/ui/button.mjs +2 -2
  35. package/dist/client/components/ui/dropdown-menu.d.mts +49 -0
  36. package/dist/client/components/ui/dropdown-menu.mjs +22 -1
  37. package/dist/client/components/ui/popover.mjs +1 -1
  38. package/dist/client/components/ui/search-input.d.mts +56 -0
  39. package/dist/client/components/ui/select.mjs +2 -2
  40. package/dist/client/components/ui/sheet.d.mts +40 -0
  41. package/dist/client/components/ui/table.d.mts +49 -0
  42. package/dist/client/components/ui/table.mjs +15 -1
  43. package/dist/client/components/ui/tooltip.d.mts +21 -0
  44. package/dist/client/contexts/focus-context.d.mts +2 -2
  45. package/dist/client/hooks/use-admin-config.mjs +20 -1
  46. package/dist/client/hooks/use-autosave.mjs +91 -0
  47. package/dist/client/hooks/use-collection.mjs +65 -23
  48. package/dist/client/hooks/use-upload.d.mts +40 -0
  49. package/dist/client/hooks/use-upload.mjs +4 -2
  50. package/dist/client/i18n/hooks.d.mts +20 -0
  51. package/dist/client/lib/utils.d.mts +6 -0
  52. package/dist/client/preview/block-scope-context.d.mts +2 -2
  53. package/dist/client/preview/preview-banner.d.mts +2 -2
  54. package/dist/client/preview/preview-field.d.mts +4 -4
  55. package/dist/client/runtime/provider.mjs +22 -3
  56. package/dist/client/scope/picker.d.mts +2 -2
  57. package/dist/client/scope/provider.d.mts +2 -2
  58. package/dist/client/styles/base.css +22 -18
  59. package/dist/client/utils/asset-url.mjs +27 -0
  60. package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
  61. package/dist/client/views/auth/auth-layout.d.mts +3 -3
  62. package/dist/client/views/auth/forgot-password-form.d.mts +2 -2
  63. package/dist/client/views/auth/login-form.d.mts +2 -2
  64. package/dist/client/views/auth/reset-password-form.d.mts +2 -2
  65. package/dist/client/views/auth/setup-form.d.mts +2 -2
  66. package/dist/client/views/collection/auto-form-fields.mjs +4 -4
  67. package/dist/client/views/collection/cells/shared/asset-thumbnail.d.mts +7 -0
  68. package/dist/client/views/collection/cells/shared/asset-thumbnail.mjs +3 -2
  69. package/dist/client/views/collection/cells/shared/cell-helpers.mjs +3 -2
  70. package/dist/client/views/collection/cells/upload-cells.mjs +2 -1
  71. package/dist/client/views/collection/document-view.d.mts +30 -0
  72. package/dist/client/views/collection/document-view.mjs +377 -0
  73. package/dist/client/views/collection/field-context.mjs +3 -2
  74. package/dist/client/views/collection/field-renderer.mjs +2 -2
  75. package/dist/client/views/collection/form-view.mjs +14 -80
  76. package/dist/client/views/collection/list-view.mjs +19 -15
  77. package/dist/client/views/collection/table-view.mjs +1 -1
  78. package/dist/client/views/layout/admin-layout-provider.mjs +4 -3
  79. package/dist/client/views/layout/admin-layout.mjs +107 -20
  80. package/dist/client/views/layout/admin-router.mjs +19 -3
  81. package/dist/client/views/layout/admin-sidebar.mjs +50 -6
  82. package/dist/client/views/layout/admin-view-layout.d.mts +36 -0
  83. package/dist/client/views/pages/accept-invite-page.d.mts +2 -2
  84. package/dist/client/views/pages/dashboard-page.d.mts +2 -2
  85. package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
  86. package/dist/client/views/pages/invite-page.d.mts +2 -2
  87. package/dist/client/views/pages/login-page.d.mts +2 -2
  88. package/dist/client/views/pages/reset-password-page.d.mts +2 -2
  89. package/dist/client/views/pages/setup-page.d.mts +2 -2
  90. package/dist/client.d.mts +17 -2
  91. package/dist/client.mjs +16 -1
  92. package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
  93. package/dist/factories.d.mts +2 -2
  94. package/dist/factories.mjs +2 -2
  95. package/dist/index.d.mts +17 -3
  96. package/dist/index.mjs +16 -1
  97. package/dist/server/augmentation/actions.d.mts +5 -0
  98. package/dist/server/augmentation/form-layout.d.mts +5 -0
  99. package/dist/server/augmentation/views.d.mts +4 -1
  100. package/dist/server/fields/blocks.mjs +4 -1
  101. package/dist/server/fields/reactive-runtime.mjs +3 -0
  102. package/dist/server/modules/admin/.generated/module.d.mts +1 -1
  103. package/dist/server/modules/admin/auth-helpers.mjs +7 -1
  104. package/dist/server/modules/admin/block/introspection.mjs +28 -4
  105. package/dist/server/modules/admin/block/prefetch.d.mts +11 -0
  106. package/dist/server/modules/admin/block/prefetch.mjs +108 -27
  107. package/dist/server/modules/admin/client/.generated/module.d.mts +68 -67
  108. package/dist/server/modules/admin/client/.generated/module.mjs +2 -0
  109. package/dist/server/modules/admin/client/views/collection-document.d.mts +6 -0
  110. package/dist/server/modules/admin/client/views/collection-document.mjs +10 -0
  111. package/dist/server/modules/admin/collections/account.d.mts +46 -46
  112. package/dist/server/modules/admin/collections/admin-locks.d.mts +57 -57
  113. package/dist/server/modules/admin/collections/admin-preferences.d.mts +42 -42
  114. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +50 -50
  115. package/dist/server/modules/admin/collections/apikey.d.mts +79 -71
  116. package/dist/server/modules/admin/collections/assets.d.mts +42 -42
  117. package/dist/server/modules/admin/collections/session.d.mts +45 -45
  118. package/dist/server/modules/admin/collections/user.d.mts +66 -66
  119. package/dist/server/modules/admin/collections/verification.d.mts +39 -39
  120. package/dist/server/modules/admin/dto/admin-config.dto.mjs +34 -4
  121. package/dist/server/modules/admin/factories.mjs +4 -34
  122. package/dist/server/modules/admin/routes/admin-config.d.mts +3 -2
  123. package/dist/server/modules/admin/routes/admin-config.mjs +18 -2
  124. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  125. package/dist/server/modules/admin/routes/execute-action.mjs +10 -4
  126. package/dist/server/modules/admin/routes/locales.d.mts +2 -2
  127. package/dist/server/modules/admin/routes/locales.mjs +1 -1
  128. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  129. package/dist/server/modules/admin/routes/preview.mjs +6 -5
  130. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  131. package/dist/server/modules/admin/routes/reactive.mjs +2 -2
  132. package/dist/server/modules/admin/routes/route-helpers.mjs +1 -1
  133. package/dist/server/modules/admin/routes/setup.d.mts +7 -7
  134. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  135. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  136. package/dist/server/modules/admin/routes/widget-data.mjs +1 -1
  137. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +27 -27
  138. package/dist/server/plugin.mjs +8 -3
  139. package/dist/server/proxy-factories.d.mts +8 -1
  140. package/dist/server/proxy-factories.mjs +33 -1
  141. package/package.json +4 -4
@@ -1,7 +1,33 @@
1
1
  import { useQuestpieQueryOptions } from "./use-questpie-query-options.mjs";
2
+ import { useEffect } from "react";
2
3
  import { useMutation, useQuery } from "@tanstack/react-query";
4
+ import { buildCollectionTopic } from "@questpie/tanstack-query";
3
5
 
4
6
  //#region src/client/hooks/use-collection.ts
7
+ function useCollectionRealtimeInvalidation({ client, collection, options, realtime, queryClient }) {
8
+ const topicSignature = JSON.stringify(collection ? buildCollectionTopic(collection, options) : null);
9
+ useEffect(() => {
10
+ if (!collection || !realtime) return;
11
+ const realtimeApi = client?.realtime;
12
+ if (!realtimeApi?.subscribe) return;
13
+ const topic = JSON.parse(topicSignature);
14
+ if (!topic) return;
15
+ return realtimeApi.subscribe(topic, () => {
16
+ queryClient.invalidateQueries({ queryKey: [
17
+ "questpie",
18
+ "collections",
19
+ "collections",
20
+ collection
21
+ ] });
22
+ });
23
+ }, [
24
+ client,
25
+ collection,
26
+ queryClient,
27
+ realtime,
28
+ topicSignature
29
+ ]);
30
+ }
5
31
  /**
6
32
  * Hook to fetch collection list with filters, sorting, pagination
7
33
  *
@@ -14,24 +40,32 @@ import { useMutation, useQuery } from "@tanstack/react-query";
14
40
  * ```
15
41
  */
16
42
  function useCollectionList(collection, options, queryOptions, realtimeOptions) {
17
- const { queryOpts, locale } = useQuestpieQueryOptions();
43
+ const { queryOpts, queryClient, locale, client } = useQuestpieQueryOptions();
18
44
  const findOptions = {
19
45
  ...options,
20
46
  locale
21
47
  };
48
+ const baseQuery = collection ? queryOpts.collections[collection].find(findOptions, { realtime: realtimeOptions?.realtime }) : {
49
+ queryKey: [
50
+ "questpie",
51
+ "collections",
52
+ "__none__",
53
+ "find"
54
+ ],
55
+ queryFn: () => ({
56
+ docs: [],
57
+ totalDocs: 0
58
+ })
59
+ };
60
+ useCollectionRealtimeInvalidation({
61
+ client,
62
+ collection: collection ? String(collection) : void 0,
63
+ options: findOptions,
64
+ realtime: realtimeOptions?.realtime,
65
+ queryClient
66
+ });
22
67
  return useQuery({
23
- ...collection ? queryOpts.collections[collection].find(findOptions, { realtime: realtimeOptions?.realtime }) : {
24
- queryKey: [
25
- "questpie",
26
- "collections",
27
- "__none__",
28
- "find"
29
- ],
30
- queryFn: () => ({
31
- docs: [],
32
- totalDocs: 0
33
- })
34
- },
68
+ ...baseQuery,
35
69
  enabled: !!collection && (queryOptions?.enabled ?? true),
36
70
  ...queryOptions
37
71
  });
@@ -54,21 +88,29 @@ function useCollectionList(collection, options, queryOptions, realtimeOptions) {
54
88
  * ```
55
89
  */
56
90
  function useCollectionCount(collection, options, queryOptions, realtimeOptions) {
57
- const { queryOpts, locale } = useQuestpieQueryOptions();
91
+ const { queryOpts, queryClient, locale, client } = useQuestpieQueryOptions();
58
92
  const countOptions = {
59
93
  ...options,
60
94
  locale
61
95
  };
96
+ const baseQuery = collection ? queryOpts.collections[collection].count(countOptions, { realtime: realtimeOptions?.realtime }) : {
97
+ queryKey: [
98
+ "questpie",
99
+ "collections",
100
+ "__none__",
101
+ "count"
102
+ ],
103
+ queryFn: () => 0
104
+ };
105
+ useCollectionRealtimeInvalidation({
106
+ client,
107
+ collection: collection ? String(collection) : void 0,
108
+ options: countOptions,
109
+ realtime: realtimeOptions?.realtime,
110
+ queryClient
111
+ });
62
112
  return useQuery({
63
- ...collection ? queryOpts.collections[collection].count(countOptions, { realtime: realtimeOptions?.realtime }) : {
64
- queryKey: [
65
- "questpie",
66
- "collections",
67
- "__none__",
68
- "count"
69
- ],
70
- queryFn: () => 0
71
- },
113
+ ...baseQuery,
72
114
  enabled: !!collection && (queryOptions?.enabled ?? true),
73
115
  ...queryOptions
74
116
  });
@@ -0,0 +1,40 @@
1
+ //#region src/client/hooks/use-upload.d.ts
2
+ interface Asset {
3
+ id: string;
4
+ key: string;
5
+ filename: string;
6
+ mimeType: string;
7
+ size: number;
8
+ visibility: "public" | "private";
9
+ url?: string;
10
+ width?: number | null;
11
+ height?: number | null;
12
+ alt?: string | null;
13
+ caption?: string | null;
14
+ createdAt?: string;
15
+ updatedAt?: string;
16
+ }
17
+ interface UploadOptions {
18
+ to?: string;
19
+ /**
20
+ * Optional destination path/folder forwarded to the collection's upload so a
21
+ * blob upload lands inside a folder (e.g. an `assets` row's required `path`).
22
+ */
23
+ path?: string;
24
+ onProgress?: (progress: number) => void;
25
+ signal?: AbortSignal;
26
+ }
27
+ interface UploadManyOptions extends UploadOptions {
28
+ onProgress?: (progress: number, fileIndex?: number) => void;
29
+ }
30
+ interface UseUploadReturn {
31
+ upload: (file: File, options?: UploadOptions) => Promise<Asset>;
32
+ uploadMany: (files: File[], options?: UploadManyOptions) => Promise<Asset[]>;
33
+ isUploading: boolean;
34
+ progress: number;
35
+ error: Error | null;
36
+ reset: () => void;
37
+ }
38
+ declare function useUpload(): UseUploadReturn;
39
+ //#endregion
40
+ export { Asset, useUpload };
@@ -29,7 +29,7 @@ function useUpload() {
29
29
  }, [uploadCollections]);
30
30
  const uploadMutation = useMutation({
31
31
  mutationFn: async ({ file, options }) => {
32
- const { to, onProgress, signal } = options ?? {};
32
+ const { to, path, onProgress, signal } = options ?? {};
33
33
  const targetCollection = resolveTargetCollection(to);
34
34
  if (!targetCollection) throw new Error(getMissingCollectionMessage());
35
35
  const collectionApi = client.collections[targetCollection];
@@ -38,6 +38,7 @@ function useUpload() {
38
38
  return {
39
39
  asset: await collectionApi.upload(file, {
40
40
  signal,
41
+ path,
41
42
  onProgress: (p) => {
42
43
  setProgress(p);
43
44
  onProgress?.(p);
@@ -56,7 +57,7 @@ function useUpload() {
56
57
  });
57
58
  const uploadManyMutation = useMutation({
58
59
  mutationFn: async ({ files, options }) => {
59
- const { to, onProgress, signal } = options ?? {};
60
+ const { to, path, onProgress, signal } = options ?? {};
60
61
  const targetCollection = resolveTargetCollection(to);
61
62
  if (!targetCollection) throw new Error(getMissingCollectionMessage());
62
63
  if (files.length === 0) return {
@@ -68,6 +69,7 @@ function useUpload() {
68
69
  setProgress(0);
69
70
  const results = await collectionApi.uploadMany(files, {
70
71
  signal,
72
+ path,
71
73
  onProgress: (p, fileIndex) => {
72
74
  setProgress(p);
73
75
  onProgress?.(p, fileIndex);
@@ -0,0 +1,20 @@
1
+ import { I18nText } from "./types.mjs";
2
+ import "react";
3
+ import "date-fns";
4
+
5
+ //#region src/client/i18n/hooks.d.ts
6
+
7
+ /**
8
+ * Resolve I18nText to string
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * function Label({ text }: { text: I18nText }) {
13
+ * const resolve = useResolveText();
14
+ * return <span>{resolve(text)}</span>;
15
+ * }
16
+ * ```
17
+ */
18
+ declare function useResolveText(): (text: I18nText | ((values: Record<string, any>) => I18nText) | undefined, fallback?: string, contextValues?: Record<string, any>) => string;
19
+ //#endregion
20
+ export { useResolveText };
@@ -0,0 +1,6 @@
1
+ import { ClassValue } from "clsx";
2
+
3
+ //#region src/client/lib/utils.d.ts
4
+ declare function cn(...inputs: ClassValue[]): string;
5
+ //#endregion
6
+ export { cn };
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import * as react_jsx_runtime21 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime59 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/preview/block-scope-context.d.ts
5
5
 
@@ -35,7 +35,7 @@ declare function BlockScopeProvider({
35
35
  blockId,
36
36
  basePath,
37
37
  children
38
- }: BlockScopeProviderProps): react_jsx_runtime21.JSX.Element;
38
+ }: BlockScopeProviderProps): react_jsx_runtime59.JSX.Element;
39
39
  /**
40
40
  * Get current block scope context.
41
41
  *
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime22 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime58 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/client/preview/preview-banner.d.ts
4
4
 
@@ -40,6 +40,6 @@ declare function PreviewBanner({
40
40
  isPreviewMode,
41
41
  className,
42
42
  exitPreviewUrl
43
- }: PreviewBannerProps): react_jsx_runtime22.JSX.Element | null;
43
+ }: PreviewBannerProps): react_jsx_runtime58.JSX.Element | null;
44
44
  //#endregion
45
45
  export { PreviewBanner, PreviewBannerProps };
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import * as react_jsx_runtime23 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime55 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/preview/preview-field.d.ts
5
5
 
@@ -68,7 +68,7 @@ declare function PreviewProvider({
68
68
  }) => void;
69
69
  onFieldValueEdited?: (payload: PreviewFieldValueEditedPayload) => void;
70
70
  children: React.ReactNode;
71
- }): react_jsx_runtime23.JSX.Element;
71
+ }): react_jsx_runtime55.JSX.Element;
72
72
  /**
73
73
  * Hook to access preview context.
74
74
  */
@@ -107,7 +107,7 @@ declare function PreviewField({
107
107
  style,
108
108
  onClick,
109
109
  onValueCommit
110
- }: PreviewFieldProps): react_jsx_runtime23.JSX.Element;
110
+ }: PreviewFieldProps): react_jsx_runtime55.JSX.Element;
111
111
  /**
112
112
  * Standalone PreviewField that works without context.
113
113
  * Useful when you can't use PreviewProvider.
@@ -131,6 +131,6 @@ declare function StandalonePreviewField({
131
131
  blockId?: string;
132
132
  fieldType?: "regular" | "block" | "relation";
133
133
  }) => void;
134
- }): react_jsx_runtime23.JSX.Element;
134
+ }): react_jsx_runtime55.JSX.Element;
135
135
  //#endregion
136
136
  export { PreviewField, PreviewFieldProps, PreviewProvider, StandalonePreviewField, usePreviewContext };
@@ -19,6 +19,13 @@ const CONTENT_LOCALE_COOKIE = "questpie_content_locale";
19
19
  /** Cookie max age (1 year) */
20
20
  const LOCALE_COOKIE_MAX_AGE = 3600 * 24 * 365;
21
21
  const LEGACY_LOCALE_COOKIE = "questpie_locale";
22
+ const PUBLIC_BRANDING_PATHS = [
23
+ "/login",
24
+ "/forgot-password",
25
+ "/reset-password",
26
+ "/accept-invite",
27
+ "/setup"
28
+ ];
22
29
  function setCookie(name, value) {
23
30
  if (typeof document === "undefined") return;
24
31
  document.cookie = `${name}=${value}; path=/; max-age=${LOCALE_COOKIE_MAX_AGE}; SameSite=Lax`;
@@ -201,13 +208,25 @@ function applyFavicon(href) {
201
208
  }
202
209
  link.href = href;
203
210
  }
211
+ function isPublicBrandingPath(basePath) {
212
+ if (typeof window === "undefined") return false;
213
+ const currentPath = window.location.pathname.replace(/\/+$/, "");
214
+ const normalizedBase = basePath.replace(/\/+$/, "");
215
+ return PUBLIC_BRANDING_PATHS.some((path) => {
216
+ const fullPath = `${normalizedBase}${path}`.replace(/\/+$/, "");
217
+ return currentPath === fullPath || currentPath.startsWith(`${fullPath}/`);
218
+ });
219
+ }
204
220
  function BrandingSync() {
205
221
  const store = useContext(AdminStoreContext);
206
222
  useEffect(() => {
207
223
  if (!store) return;
208
- const client = store.getState().client;
209
- if (!client || !client.routes?.getAdminConfig) return;
210
- client.routes.getAdminConfig().then((config) => {
224
+ const state = store.getState();
225
+ const routes = state.client?.routes;
226
+ if (!routes) return;
227
+ const configRequest = isPublicBrandingPath(state.basePath) && typeof routes.getPublicAdminConfig === "function" ? routes.getPublicAdminConfig() : routes.getAdminConfig?.();
228
+ if (!configRequest) return;
229
+ configRequest.then((config) => {
211
230
  const branding = config?.branding;
212
231
  if (!branding) return;
213
232
  const next = {};
@@ -1,5 +1,5 @@
1
1
  import { ScopePickerProps } from "./types.mjs";
2
- import * as react_jsx_runtime26 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime61 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/scope/picker.d.ts
5
5
 
@@ -48,6 +48,6 @@ declare function ScopePicker({
48
48
  clearText,
49
49
  className,
50
50
  compact
51
- }: ScopePickerProps): react_jsx_runtime26.JSX.Element;
51
+ }: ScopePickerProps): react_jsx_runtime61.JSX.Element;
52
52
  //#endregion
53
53
  export { ScopePicker };
@@ -1,5 +1,5 @@
1
1
  import { ScopeContextValue, ScopeProviderProps } from "./types.mjs";
2
- import * as react_jsx_runtime27 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime60 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/scope/provider.d.ts
5
5
 
@@ -29,7 +29,7 @@ declare function ScopeProvider({
29
29
  headerName,
30
30
  storageKey,
31
31
  defaultScope
32
- }: ScopeProviderProps): react_jsx_runtime27.JSX.Element;
32
+ }: ScopeProviderProps): react_jsx_runtime60.JSX.Element;
33
33
  /**
34
34
  * Hook to access the current scope context.
35
35
  *
@@ -596,29 +596,31 @@
596
596
  padding: 0;
597
597
  }
598
598
 
599
- /* Slash command menu — generous spacing */
599
+ /* Slash command menu — compact command density */
600
600
  .qp-rich-text-editor__slash {
601
601
  display: flex;
602
602
  flex-direction: column;
603
- gap: 0.25rem;
604
- min-width: 300px;
605
- max-width: min(380px, calc(100vw - 2rem));
606
- padding: 0.5rem;
603
+ gap: 0.125rem;
604
+ min-width: 280px;
605
+ max-width: min(340px, calc(100vw - 2rem));
606
+ max-height: min(19rem, calc(100vh - 4rem));
607
+ overflow-y: auto;
608
+ padding: 0.375rem;
607
609
  }
608
610
 
609
611
  .qp-rich-text-editor__slash-item {
610
612
  display: flex;
611
613
  width: 100%;
612
- min-height: 3rem;
614
+ min-height: 2.25rem;
613
615
  align-items: center;
614
- gap: 0.75rem;
616
+ gap: 0.5rem;
615
617
  border: 1px solid transparent;
616
- border-radius: var(--control-radius-inner);
618
+ border-radius: var(--radius-sm);
617
619
  background: transparent;
618
- padding: 0.5rem 0.625rem;
620
+ padding: 0.3125rem 0.5rem;
619
621
  color: var(--foreground);
620
622
  font-size: 0.8125rem;
621
- line-height: 1.3;
623
+ line-height: 1.2;
622
624
  text-align: left;
623
625
  transition:
624
626
  background-color 150ms ease,
@@ -635,8 +637,8 @@
635
637
 
636
638
  .qp-rich-text-editor__slash-icon {
637
639
  display: inline-flex;
638
- width: 2.25rem;
639
- height: 2.25rem;
640
+ width: 1.5rem;
641
+ height: 1.5rem;
640
642
  flex: 0 0 auto;
641
643
  align-items: center;
642
644
  justify-content: center;
@@ -651,25 +653,27 @@
651
653
  min-width: 0;
652
654
  flex: 1;
653
655
  flex-direction: column;
654
- gap: 0.125rem;
656
+ gap: 0.0625rem;
655
657
  }
656
658
 
657
659
  .qp-rich-text-editor__slash-title {
658
660
  font-family: var(--font-chrome);
659
- font-weight: 600;
661
+ font-weight: 500;
660
662
  }
661
663
 
662
664
  .qp-rich-text-editor__slash-description {
663
665
  color: var(--muted-foreground);
666
+ font-size: 0.6875rem;
667
+ line-height: 1.25;
664
668
  overflow: hidden;
665
669
  text-overflow: ellipsis;
666
670
  white-space: nowrap;
667
671
  }
668
672
 
669
673
  .qp-rich-text-editor__slash-group {
670
- padding: 0.375rem 0.625rem 0.125rem;
674
+ padding: 0.3125rem 0.5rem 0.125rem;
671
675
  font-family: var(--font-chrome);
672
- font-size: 0.6875rem;
676
+ font-size: 0.625rem;
673
677
  font-weight: 600;
674
678
  letter-spacing: 0.04em;
675
679
  text-transform: uppercase;
@@ -677,7 +681,7 @@
677
681
  }
678
682
 
679
683
  .qp-rich-text-editor__slash-empty {
680
- padding: 0.75rem;
684
+ padding: 0.625rem;
681
685
  font-size: 0.8125rem;
682
686
  color: var(--muted-foreground);
683
687
  }
@@ -706,7 +710,7 @@
706
710
  }
707
711
 
708
712
  .qp-rich-text-editor__slash {
709
- min-width: min(300px, calc(100vw - 2rem));
713
+ min-width: min(280px, calc(100vw - 2rem));
710
714
  }
711
715
  }
712
716
 
@@ -0,0 +1,27 @@
1
+ //#region src/client/utils/asset-url.ts
2
+ const STORAGE_FILE_PATH_PATTERN = /^\/api\/[^/]+\/files(?:\/|$)/;
3
+ function isStorageFilePath(pathname) {
4
+ return STORAGE_FILE_PATH_PATTERN.test(pathname);
5
+ }
6
+ function currentOrigin() {
7
+ if (typeof window === "undefined" || !window.location?.origin) return null;
8
+ return window.location.origin;
9
+ }
10
+ function resolveAssetUrl(url) {
11
+ if (typeof url !== "string") return void 0;
12
+ const trimmed = url.trim();
13
+ if (!trimmed) return void 0;
14
+ if (trimmed.startsWith("/")) return trimmed;
15
+ const origin = currentOrigin();
16
+ if (!origin) return trimmed;
17
+ try {
18
+ const parsed = new URL(trimmed);
19
+ if (isStorageFilePath(parsed.pathname) && parsed.origin === origin) return `${parsed.pathname}${parsed.search}${parsed.hash}`;
20
+ } catch {
21
+ return trimmed;
22
+ }
23
+ return trimmed;
24
+ }
25
+
26
+ //#endregion
27
+ export { resolveAssetUrl };
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime0 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime34 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/client/views/auth/accept-invite-form.d.ts
4
4
  /**
@@ -67,6 +67,6 @@ declare function AcceptInviteForm({
67
67
  className,
68
68
  error,
69
69
  minPasswordLength
70
- }: AcceptInviteFormProps): react_jsx_runtime0.JSX.Element;
70
+ }: AcceptInviteFormProps): react_jsx_runtime34.JSX.Element;
71
71
  //#endregion
72
72
  export { AcceptInviteForm };
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import * as react_jsx_runtime1 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime35 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/views/auth/auth-layout.d.ts
5
5
 
@@ -26,7 +26,7 @@ declare function AuthDefaultLogo({
26
26
  brandName
27
27
  }: {
28
28
  brandName: string;
29
- }): react_jsx_runtime1.JSX.Element;
29
+ }): react_jsx_runtime35.JSX.Element;
30
30
  /**
31
31
  * Minimal split layout for authentication pages (login, register, forgot password, etc.)
32
32
  *
@@ -50,6 +50,6 @@ declare function AuthDefaultLogo({
50
50
  * </AuthLayout>
51
51
  * ```
52
52
  */
53
- declare function AuthLayout(props: AuthLayoutProps): react_jsx_runtime1.JSX.Element;
53
+ declare function AuthLayout(props: AuthLayoutProps): react_jsx_runtime35.JSX.Element;
54
54
  //#endregion
55
55
  export { AuthDefaultLogo, AuthLayout, AuthLayoutProps };
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime3 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime37 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/client/views/auth/forgot-password-form.d.ts
4
4
  /**
@@ -53,6 +53,6 @@ declare function ForgotPasswordForm({
53
53
  defaultValues,
54
54
  className,
55
55
  error
56
- }: ForgotPasswordFormProps): react_jsx_runtime3.JSX.Element;
56
+ }: ForgotPasswordFormProps): react_jsx_runtime37.JSX.Element;
57
57
  //#endregion
58
58
  export { ForgotPasswordForm };
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime4 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime38 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/client/views/auth/login-form.d.ts
4
4
  /**
@@ -70,6 +70,6 @@ declare function LoginForm({
70
70
  className,
71
71
  error,
72
72
  minPasswordLength
73
- }: LoginFormProps): react_jsx_runtime4.JSX.Element;
73
+ }: LoginFormProps): react_jsx_runtime38.JSX.Element;
74
74
  //#endregion
75
75
  export { LoginForm };
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime5 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime39 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/client/views/auth/reset-password-form.d.ts
4
4
  /**
@@ -60,6 +60,6 @@ declare function ResetPasswordForm({
60
60
  minPasswordLength,
61
61
  className,
62
62
  error
63
- }: ResetPasswordFormProps): react_jsx_runtime5.JSX.Element;
63
+ }: ResetPasswordFormProps): react_jsx_runtime39.JSX.Element;
64
64
  //#endregion
65
65
  export { ResetPasswordForm };
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime6 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime40 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/client/views/auth/setup-form.d.ts
4
4
  /**
@@ -55,6 +55,6 @@ declare function SetupForm({
55
55
  className,
56
56
  error,
57
57
  minPasswordLength
58
- }: SetupFormProps): react_jsx_runtime6.JSX.Element;
58
+ }: SetupFormProps): react_jsx_runtime40.JSX.Element;
59
59
  //#endregion
60
60
  export { SetupForm, SetupFormValues };
@@ -274,18 +274,18 @@ function SectionLayoutRenderer({ section, index, fields, collection, mode = "col
274
274
  const value = `section-${index}`;
275
275
  return /* @__PURE__ */ jsx(Accordion, {
276
276
  defaultValue: section.defaultCollapsed !== true ? [value] : [],
277
- className: "w-full",
277
+ className: "qa-form-fields__section qa-form-fields__section--collapsible panel-surface bg-card overflow-hidden",
278
278
  children: /* @__PURE__ */ jsxs(AccordionItem, {
279
279
  value,
280
- className: "border-transparent px-4",
280
+ className: "border-none px-0 data-open:bg-transparent",
281
281
  children: [/* @__PURE__ */ jsx(AccordionTrigger, {
282
- className: "hover:no-underline",
282
+ className: "hover:bg-surface-low aria-expanded:bg-surface-low min-h-12 px-4 py-3 hover:no-underline",
283
283
  children: /* @__PURE__ */ jsx("span", {
284
284
  className: "font-semibold",
285
285
  children: resolveText(section.label, "Section", formValues)
286
286
  })
287
287
  }), /* @__PURE__ */ jsxs(AccordionContent, {
288
- className: "pt-2 pb-4",
288
+ className: "border-border-subtle border-t px-4 pt-3 pb-4",
289
289
  children: [section.description && /* @__PURE__ */ jsx("p", {
290
290
  className: "text-muted-foreground mb-4 text-sm text-pretty",
291
291
  children: resolveText(section.description, "", formValues)
@@ -0,0 +1,7 @@
1
+ import "react/jsx-runtime";
2
+
3
+ //#region src/client/views/collection/cells/shared/asset-thumbnail.d.ts
4
+
5
+ declare function getFileIcon(mimeType?: string): string;
6
+ //#endregion
7
+ export { getFileIcon };
@@ -1,5 +1,6 @@
1
1
  import { cn } from "../../../../lib/utils.mjs";
2
2
  import { Button } from "../../../../components/ui/button.mjs";
3
+ import { resolveAssetUrl } from "../../../../utils/asset-url.mjs";
3
4
  import { Icon } from "@iconify/react";
4
5
  import { jsx, jsxs } from "react/jsx-runtime";
5
6
 
@@ -51,7 +52,7 @@ function AssetThumbnail({ asset, size = "sm", showFilename = false, showControls
51
52
  })
52
53
  });
53
54
  const assetObj = asset;
54
- const url = assetObj.url;
55
+ const url = resolveAssetUrl(assetObj.url);
55
56
  const filename = assetObj.filename;
56
57
  const mimeType = assetObj.mimeType;
57
58
  const alt = assetObj.alt;
@@ -223,4 +224,4 @@ function AssetThumbnail({ asset, size = "sm", showFilename = false, showControls
223
224
  }
224
225
 
225
226
  //#endregion
226
- export { AssetThumbnail };
227
+ export { AssetThumbnail, getFileIcon };
@@ -1,4 +1,5 @@
1
1
  import { formatLabel } from "../../../../lib/utils.mjs";
2
+ import { resolveAssetUrl } from "../../../../utils/asset-url.mjs";
2
3
 
3
4
  //#region src/client/views/collection/cells/shared/cell-helpers.ts
4
5
  function getNestedValue(item, path) {
@@ -15,9 +16,9 @@ function toImageUrl(value) {
15
16
  if (!value || typeof value !== "object") return null;
16
17
  const obj = value;
17
18
  const url = obj.url;
18
- if (typeof url === "string" && url.length > 0) return url;
19
+ if (typeof url === "string" && url.length > 0) return resolveAssetUrl(url) ?? null;
19
20
  const src = obj.src;
20
- if (typeof src === "string" && src.length > 0) return src;
21
+ if (typeof src === "string" && src.length > 0) return resolveAssetUrl(src) ?? null;
21
22
  return null;
22
23
  }
23
24
  /**