canopycms 0.0.57 → 0.0.58
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/dist/ai/json-to-markdown.js +74 -3
- package/dist/ai/json-to-markdown.js.map +1 -1
- package/dist/api/__test__/mock-client.d.ts +9 -5
- package/dist/api/__test__/mock-client.d.ts.map +1 -1
- package/dist/api/__test__/mock-client.js +15 -7
- package/dist/api/__test__/mock-client.js.map +1 -1
- package/dist/api/assets.d.ts +100 -36
- package/dist/api/assets.d.ts.map +1 -1
- package/dist/api/assets.js +338 -51
- package/dist/api/assets.js.map +1 -1
- package/dist/api/client.d.ts +16 -5
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +24 -6
- package/dist/api/client.js.map +1 -1
- package/dist/api/entries.d.ts +4 -4
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/route-builder.d.ts +9 -0
- package/dist/api/route-builder.d.ts.map +1 -1
- package/dist/api/route-builder.js +1 -0
- package/dist/api/route-builder.js.map +1 -1
- package/dist/api/schema.d.ts +4 -4
- package/dist/api/types.d.ts +11 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/assets/asset-prefixes.d.ts +31 -0
- package/dist/assets/asset-prefixes.d.ts.map +1 -0
- package/dist/assets/asset-prefixes.js +20 -0
- package/dist/assets/asset-prefixes.js.map +1 -0
- package/dist/assets/asset-src.d.ts +23 -0
- package/dist/assets/asset-src.d.ts.map +1 -0
- package/dist/assets/asset-src.js +28 -0
- package/dist/assets/asset-src.js.map +1 -0
- package/dist/assets/asset-url.d.ts +35 -0
- package/dist/assets/asset-url.d.ts.map +1 -0
- package/dist/assets/asset-url.js +77 -0
- package/dist/assets/asset-url.js.map +1 -0
- package/dist/assets/factory.d.ts +18 -0
- package/dist/assets/factory.d.ts.map +1 -0
- package/dist/assets/factory.js +42 -0
- package/dist/assets/factory.js.map +1 -0
- package/dist/assets/finalize.d.ts +36 -0
- package/dist/assets/finalize.d.ts.map +1 -0
- package/dist/assets/finalize.js +72 -0
- package/dist/assets/finalize.js.map +1 -0
- package/dist/assets/index.d.ts +20 -0
- package/dist/assets/index.d.ts.map +1 -0
- package/dist/assets/index.js +19 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/assets/keys.d.ts +56 -0
- package/dist/assets/keys.d.ts.map +1 -0
- package/dist/assets/keys.js +88 -0
- package/dist/assets/keys.js.map +1 -0
- package/dist/assets/pipeline.d.ts +50 -0
- package/dist/assets/pipeline.d.ts.map +1 -0
- package/dist/assets/pipeline.js +202 -0
- package/dist/assets/pipeline.js.map +1 -0
- package/dist/assets/store-local.d.ts +84 -0
- package/dist/assets/store-local.d.ts.map +1 -0
- package/dist/assets/store-local.js +254 -0
- package/dist/assets/store-local.js.map +1 -0
- package/dist/assets/store-s3.d.ts +71 -0
- package/dist/assets/store-s3.d.ts.map +1 -0
- package/dist/assets/store-s3.js +183 -0
- package/dist/assets/store-s3.js.map +1 -0
- package/dist/assets/svg-sanitizer.d.ts +37 -0
- package/dist/assets/svg-sanitizer.d.ts.map +1 -0
- package/dist/assets/svg-sanitizer.js +222 -0
- package/dist/assets/svg-sanitizer.js.map +1 -0
- package/dist/assets/transform-directives.d.ts +97 -0
- package/dist/assets/transform-directives.d.ts.map +1 -0
- package/dist/assets/transform-directives.js +267 -0
- package/dist/assets/transform-directives.js.map +1 -0
- package/dist/assets/transform.d.ts +51 -0
- package/dist/assets/transform.d.ts.map +1 -0
- package/dist/assets/transform.js +190 -0
- package/dist/assets/transform.js.map +1 -0
- package/dist/assets/types.d.ts +82 -0
- package/dist/assets/types.d.ts.map +1 -0
- package/dist/assets/types.js +10 -0
- package/dist/assets/types.js.map +1 -0
- package/dist/authorization/groups/schema.d.ts +2 -2
- package/dist/authorization/permissions/schema.d.ts +2 -2
- package/dist/cli/generate-ai-content.js +92 -7
- package/dist/cli/init.js +16 -4
- package/dist/config/helpers.d.ts.map +1 -1
- package/dist/config/helpers.js +2 -1
- package/dist/config/helpers.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schemas/config.d.ts +10 -0
- package/dist/config/schemas/config.d.ts.map +1 -1
- package/dist/config/schemas/field.d.ts +38 -3
- package/dist/config/schemas/field.d.ts.map +1 -1
- package/dist/config/schemas/field.js +18 -0
- package/dist/config/schemas/field.js.map +1 -1
- package/dist/config/schemas/media.d.ts +8 -0
- package/dist/config/schemas/media.d.ts.map +1 -1
- package/dist/config/schemas/media.js +9 -0
- package/dist/config/schemas/media.js.map +1 -1
- package/dist/config/types.d.ts +43 -3
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +4 -1
- package/dist/config/types.js.map +1 -1
- package/dist/editor/CanopyEditor.d.ts.map +1 -1
- package/dist/editor/CanopyEditor.js +1 -1
- package/dist/editor/CanopyEditor.js.map +1 -1
- package/dist/editor/Editor.d.ts +2 -0
- package/dist/editor/Editor.d.ts.map +1 -1
- package/dist/editor/Editor.js +82 -80
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FormRenderer.d.ts.map +1 -1
- package/dist/editor/FormRenderer.js +9 -0
- package/dist/editor/FormRenderer.js.map +1 -1
- package/dist/editor/components/EditorSidebar.d.ts +6 -1
- package/dist/editor/components/EditorSidebar.d.ts.map +1 -1
- package/dist/editor/components/EditorSidebar.js +4 -3
- package/dist/editor/components/EditorSidebar.js.map +1 -1
- package/dist/editor/context/AssetContext.d.ts +25 -0
- package/dist/editor/context/AssetContext.d.ts.map +1 -0
- package/dist/editor/context/AssetContext.js +26 -0
- package/dist/editor/context/AssetContext.js.map +1 -0
- package/dist/editor/context/index.d.ts +1 -0
- package/dist/editor/context/index.d.ts.map +1 -1
- package/dist/editor/context/index.js +1 -0
- package/dist/editor/context/index.js.map +1 -1
- package/dist/editor/fields/ImageField.d.ts +22 -0
- package/dist/editor/fields/ImageField.d.ts.map +1 -0
- package/dist/editor/fields/ImageField.js +100 -0
- package/dist/editor/fields/ImageField.js.map +1 -0
- package/dist/editor/fields/MarkdownField.d.ts.map +1 -1
- package/dist/editor/fields/MarkdownField.js +30 -4
- package/dist/editor/fields/MarkdownField.js.map +1 -1
- package/dist/editor/fields/MdxImageDialog.d.ts +16 -0
- package/dist/editor/fields/MdxImageDialog.d.ts.map +1 -0
- package/dist/editor/fields/MdxImageDialog.js +73 -0
- package/dist/editor/fields/MdxImageDialog.js.map +1 -0
- package/dist/editor/media/AssetCard.d.ts +21 -0
- package/dist/editor/media/AssetCard.d.ts.map +1 -0
- package/dist/editor/media/AssetCard.js +41 -0
- package/dist/editor/media/AssetCard.js.map +1 -0
- package/dist/editor/media/CropStep.d.ts +26 -0
- package/dist/editor/media/CropStep.d.ts.map +1 -0
- package/dist/editor/media/CropStep.js +38 -0
- package/dist/editor/media/CropStep.js.map +1 -0
- package/dist/editor/media/MediaLibrary.d.ts +22 -0
- package/dist/editor/media/MediaLibrary.d.ts.map +1 -0
- package/dist/editor/media/MediaLibrary.js +17 -0
- package/dist/editor/media/MediaLibrary.js.map +1 -0
- package/dist/editor/media/MediaLibraryBody.d.ts +17 -0
- package/dist/editor/media/MediaLibraryBody.d.ts.map +1 -0
- package/dist/editor/media/MediaLibraryBody.js +133 -0
- package/dist/editor/media/MediaLibraryBody.js.map +1 -0
- package/dist/editor/media/crop-math.d.ts +37 -0
- package/dist/editor/media/crop-math.d.ts.map +1 -0
- package/dist/editor/media/crop-math.js +62 -0
- package/dist/editor/media/crop-math.js.map +1 -0
- package/dist/editor/media/upload-asset.d.ts +31 -0
- package/dist/editor/media/upload-asset.d.ts.map +1 -0
- package/dist/editor/media/upload-asset.js +56 -0
- package/dist/editor/media/upload-asset.js.map +1 -0
- package/dist/editor/media/upload-constants.d.ts +27 -0
- package/dist/editor/media/upload-constants.d.ts.map +1 -0
- package/dist/editor/media/upload-constants.js +40 -0
- package/dist/editor/media/upload-constants.js.map +1 -0
- package/dist/editor/media/useAssetUpload.d.ts +19 -0
- package/dist/editor/media/useAssetUpload.d.ts.map +1 -0
- package/dist/editor/media/useAssetUpload.js +53 -0
- package/dist/editor/media/useAssetUpload.js.map +1 -0
- package/dist/editor/media/xhr-upload.d.ts +20 -0
- package/dist/editor/media/xhr-upload.d.ts.map +1 -0
- package/dist/editor/media/xhr-upload.js +42 -0
- package/dist/editor/media/xhr-upload.js.map +1 -0
- package/dist/editor/theme.d.ts +2 -1
- package/dist/editor/theme.d.ts.map +1 -1
- package/dist/editor/theme.js +1 -0
- package/dist/editor/theme.js.map +1 -1
- package/dist/entry-schema.d.ts +19 -0
- package/dist/entry-schema.d.ts.map +1 -1
- package/dist/entry-schema.js.map +1 -1
- package/dist/http/handler.d.ts +5 -2
- package/dist/http/handler.d.ts.map +1 -1
- package/dist/http/handler.js +13 -4
- package/dist/http/handler.js.map +1 -1
- package/dist/http/index.d.ts +2 -2
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +1 -1
- package/dist/http/index.js.map +1 -1
- package/dist/http/router.d.ts +14 -1
- package/dist/http/router.d.ts.map +1 -1
- package/dist/http/router.js +4 -1
- package/dist/http/router.js.map +1 -1
- package/dist/http/types.d.ts +40 -0
- package/dist/http/types.d.ts.map +1 -1
- package/dist/http/types.js +9 -0
- package/dist/http/types.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/schema/schema-store.d.ts +2 -2
- package/dist/server.d.ts +45 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +41 -0
- package/dist/server.js.map +1 -1
- package/dist/validation/entry-validator.d.ts.map +1 -1
- package/dist/validation/entry-validator.js +56 -1
- package/dist/validation/entry-validator.js.map +1 -1
- package/package.json +13 -1
- package/dist/asset-store.d.ts +0 -32
- package/dist/asset-store.d.ts.map +0 -1
- package/dist/asset-store.js +0 -85
- package/dist/asset-store.js.map +0 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Asset Context
|
|
5
|
+
*
|
|
6
|
+
* Carries `media.publicBaseUrl` (see CanopyClientConfig.assetBaseUrl) down to
|
|
7
|
+
* every asset-URL-building component - MediaLibrary, ImageField, and the MDX
|
|
8
|
+
* image dialog. A context (rather than a prop) because the MDX dialog is
|
|
9
|
+
* rendered deep inside MDXEditor's lazy-loaded internals via `imagePlugin`'s
|
|
10
|
+
* `ImageDialog` param, with no prop channel of its own (the same reason
|
|
11
|
+
* `EntryLinkContext` exists for the entry-link toolbar button). Unlike
|
|
12
|
+
* `ApiClientContext`, reading this without a provider is safe and intentional:
|
|
13
|
+
* no provider means root-relative URLs, which is the correct default for the
|
|
14
|
+
* common case (editor and site share an origin).
|
|
15
|
+
*/
|
|
16
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
17
|
+
const AssetContext = createContext({});
|
|
18
|
+
export function AssetContextProvider({ baseUrl, children }) {
|
|
19
|
+
const value = useMemo(() => ({ baseUrl }), [baseUrl]);
|
|
20
|
+
return _jsx(AssetContext.Provider, { value: value, children: children });
|
|
21
|
+
}
|
|
22
|
+
/** Safe to call outside a provider - returns `{ baseUrl: undefined }` (root-relative URLs). */
|
|
23
|
+
export function useAssetContext() {
|
|
24
|
+
return useContext(AssetContext);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=AssetContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetContext.js","sourceRoot":"","sources":["../../../src/editor/context/AssetContext.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;;GAYG;AAEH,OAAc,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAOjE,MAAM,YAAY,GAAG,aAAa,CAAoB,EAAE,CAAC,CAAA;AAMzD,MAAM,UAAU,oBAAoB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAA6B;IACnF,MAAM,KAAK,GAAG,OAAO,CAAoB,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IACxE,OAAO,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAyB,CAAA;AAChF,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,CAAC,YAAY,CAAC,CAAA;AACjC,CAAC"}
|
|
@@ -16,5 +16,6 @@
|
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
18
|
export { ApiClientProvider, useApiClient, useOptionalApiClient, type ApiClient, type ApiClientProviderProps, } from './ApiClientContext';
|
|
19
|
+
export { AssetContextProvider, useAssetContext, type AssetContextValue, type AssetContextProviderProps, } from './AssetContext';
|
|
19
20
|
export { EditorStateProvider, useEditorState, useEditorLoading, useEditorModals, useEditorPreview, type EditorState, type EditorStateActions, type EditorStateContextValue, type EditorStateProviderProps, type LoadingState, type ModalState, type PreviewState, } from './EditorStateContext';
|
|
20
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/editor/context/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,sBAAsB,GAC5B,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/editor/context/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,sBAAsB,GAC5B,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,GAC/B,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAA"}
|
|
@@ -16,5 +16,6 @@
|
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
18
|
export { ApiClientProvider, useApiClient, useOptionalApiClient, } from './ApiClientContext.js';
|
|
19
|
+
export { AssetContextProvider, useAssetContext, } from './AssetContext.js';
|
|
19
20
|
export { EditorStateProvider, useEditorState, useEditorLoading, useEditorModals, useEditorPreview, } from './EditorStateContext.js';
|
|
20
21
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/editor/context/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,GAGrB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GAQjB,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/editor/context/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,GAGrB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,oBAAoB,EACpB,eAAe,GAGhB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GAQjB,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ImageFieldValue } from '../../config';
|
|
3
|
+
export interface ImageFieldErrors {
|
|
4
|
+
src?: string;
|
|
5
|
+
alt?: string;
|
|
6
|
+
crop?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ImageFieldProps {
|
|
9
|
+
id?: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
value: ImageFieldValue | undefined;
|
|
12
|
+
onChange: (value: ImageFieldValue | undefined) => void;
|
|
13
|
+
/** "W:H" aspect ratio - when set, picking/uploading a new image opens the crop step before the value commits, and a "Crop" button appears on the filled state. */
|
|
14
|
+
aspect?: string;
|
|
15
|
+
/** Allow empty alt text. Default: required (accessibility). */
|
|
16
|
+
altOptional?: boolean;
|
|
17
|
+
dataCanopyField?: string;
|
|
18
|
+
errors?: ImageFieldErrors;
|
|
19
|
+
}
|
|
20
|
+
export declare const ImageField: React.FC<ImageFieldProps>;
|
|
21
|
+
export default ImageField;
|
|
22
|
+
//# sourceMappingURL=ImageField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageField.d.ts","sourceRoot":"","sources":["../../../src/editor/fields/ImageField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA6C,MAAM,OAAO,CAAA;AASjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAQnD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,eAAe,GAAG,SAAS,CAAA;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,KAAK,IAAI,CAAA;IACtD,kKAAkK;IAClK,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAA;CAC1B;AAOD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAsOhD,CAAA;AAED,eAAe,UAAU,CAAA"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useRef, useState } from 'react';
|
|
4
|
+
import { Alert, Button, Group, Stack, Text, TextInput } from '@mantine/core';
|
|
5
|
+
import { Dropzone } from '@mantine/dropzone';
|
|
6
|
+
import { IconAlertCircle, IconPhoto, IconUpload } from '@tabler/icons-react';
|
|
7
|
+
import { assetUrl } from '../../assets/asset-url.js';
|
|
8
|
+
import { useAssetContext } from '../context/index.js';
|
|
9
|
+
import { CropStep } from '../media/CropStep.js';
|
|
10
|
+
import { parseAspectRatio } from '../media/crop-math.js';
|
|
11
|
+
import { MediaLibrary } from '../media/MediaLibrary.js';
|
|
12
|
+
import { useAssetUpload } from '../media/useAssetUpload.js';
|
|
13
|
+
import { ACCEPTED_IMAGE_MIME_TYPES, MAX_UPLOAD_BYTES } from '../media/upload-constants.js';
|
|
14
|
+
const PREVIEW_WIDTH = 320;
|
|
15
|
+
export const ImageField = ({ id, label, value, onChange, aspect, altOptional, dataCanopyField, errors, }) => {
|
|
16
|
+
const generatedId = useId();
|
|
17
|
+
const inputId = id ?? generatedId;
|
|
18
|
+
const { baseUrl } = useAssetContext();
|
|
19
|
+
const upload = useAssetUpload();
|
|
20
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
21
|
+
const [cropRequest, setCropRequest] = useState(null);
|
|
22
|
+
// Dropzone-level rejections (wrong type/too large) never reach useAssetUpload
|
|
23
|
+
// (Dropzone filters them out before onDrop fires), so they need their own
|
|
24
|
+
// error slot rather than piggybacking on upload.error.
|
|
25
|
+
const [dropError, setDropError] = useState(null);
|
|
26
|
+
const altInputRef = useRef(null);
|
|
27
|
+
const justCommittedRef = useRef(false);
|
|
28
|
+
const aspectRatio = parseAspectRatio(aspect);
|
|
29
|
+
const hasValue = !!value?.src;
|
|
30
|
+
// Focus the alt input right after a new image commits (pick or upload),
|
|
31
|
+
// so the editor's next keystroke goes straight into the field most likely
|
|
32
|
+
// to need attention.
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (justCommittedRef.current) {
|
|
35
|
+
justCommittedRef.current = false;
|
|
36
|
+
altInputRef.current?.focus();
|
|
37
|
+
}
|
|
38
|
+
}, [value?.src]);
|
|
39
|
+
const commitAsset = (asset, crop) => {
|
|
40
|
+
justCommittedRef.current = true;
|
|
41
|
+
onChange({
|
|
42
|
+
src: asset.src,
|
|
43
|
+
alt: '',
|
|
44
|
+
...(asset.width !== undefined ? { width: asset.width } : {}),
|
|
45
|
+
...(asset.height !== undefined ? { height: asset.height } : {}),
|
|
46
|
+
...(crop ? { crop } : {}),
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
const handleAssetReady = (asset) => {
|
|
50
|
+
setPickerOpen(false);
|
|
51
|
+
if (aspectRatio !== undefined) {
|
|
52
|
+
setCropRequest({ kind: 'new', asset });
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
commitAsset(asset);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const handleDrop = async (files) => {
|
|
59
|
+
const file = files[0];
|
|
60
|
+
if (!file)
|
|
61
|
+
return;
|
|
62
|
+
const asset = await upload.upload(file);
|
|
63
|
+
if (asset)
|
|
64
|
+
handleAssetReady(asset);
|
|
65
|
+
};
|
|
66
|
+
const handleReject = (rejections) => {
|
|
67
|
+
setDropError(rejections[0]?.errors[0]?.message ?? 'File rejected');
|
|
68
|
+
};
|
|
69
|
+
const handleAltChange = (nextAlt) => {
|
|
70
|
+
if (!value)
|
|
71
|
+
return;
|
|
72
|
+
onChange({ ...value, alt: nextAlt });
|
|
73
|
+
};
|
|
74
|
+
const handleRemove = () => {
|
|
75
|
+
onChange(undefined);
|
|
76
|
+
};
|
|
77
|
+
const handleCropConfirm = (rect) => {
|
|
78
|
+
if (cropRequest?.kind === 'new') {
|
|
79
|
+
commitAsset(cropRequest.asset, rect);
|
|
80
|
+
}
|
|
81
|
+
else if (cropRequest?.kind === 'existing' && value) {
|
|
82
|
+
onChange({ ...value, crop: rect });
|
|
83
|
+
}
|
|
84
|
+
setCropRequest(null);
|
|
85
|
+
};
|
|
86
|
+
const cropImageSrc = cropRequest?.kind === 'new'
|
|
87
|
+
? assetUrl({ src: cropRequest.asset.src }, { baseUrl })
|
|
88
|
+
: cropRequest?.kind === 'existing' && value
|
|
89
|
+
? assetUrl({ src: value.src }, { baseUrl })
|
|
90
|
+
: '';
|
|
91
|
+
const cropInitial = cropRequest?.kind === 'existing' ? value?.crop : undefined;
|
|
92
|
+
return (_jsxs(Stack, { gap: 4, "data-canopy-field": dataCanopyField, "data-testid": `image-field-${dataCanopyField}`, children: [label && (_jsx(Text, { size: "sm", fw: 500, children: label })), !hasValue ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Dropzone, { onDrop: (files) => void handleDrop(files), onReject: handleReject, maxSize: MAX_UPLOAD_BYTES, accept: ACCEPTED_IMAGE_MIME_TYPES, loading: upload.uploading, multiple: false, "data-testid": `image-field-dropzone-${dataCanopyField}`, children: _jsxs(Group, { justify: "center", gap: "xs", py: "xs", style: { pointerEvents: 'none' }, children: [_jsx(IconUpload, { size: 18 }), _jsx(Text, { size: "xs", children: "Drop an image here, or click to browse" })] }) }), _jsx(Group, { justify: "center", children: _jsx(Button, { variant: "light", size: "xs", leftSection: _jsx(IconPhoto, { size: 14 }), onClick: () => setPickerOpen(true), "data-testid": `image-field-browse-library-${dataCanopyField}`, children: "Browse library" }) }), (upload.error || dropError) && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "red", children: upload.error || dropError })), errors?.src && (_jsx(Text, { size: "xs", c: "red", children: errors.src }))] })) : (_jsxs(Stack, { gap: "xs", children: [_jsx("img", { src: assetUrl({ src: value.src }, { width: PREVIEW_WIDTH, crop: value.crop, baseUrl }), alt: value.alt, style: {
|
|
93
|
+
maxWidth: PREVIEW_WIDTH,
|
|
94
|
+
maxHeight: PREVIEW_WIDTH,
|
|
95
|
+
borderRadius: 'var(--mantine-radius-sm)',
|
|
96
|
+
display: 'block',
|
|
97
|
+
} }), errors?.src && (_jsx(Text, { size: "xs", c: "red", children: errors.src })), _jsx(TextInput, { id: inputId, ref: altInputRef, label: "Alt text", required: !altOptional, value: value.alt, onChange: (event) => handleAltChange(event.currentTarget.value), error: errors?.alt, size: "sm", "data-testid": `image-field-alt-${dataCanopyField}` }), _jsxs(Group, { gap: "xs", children: [_jsx(Button, { variant: "light", size: "xs", onClick: () => setPickerOpen(true), "data-testid": `image-field-replace-${dataCanopyField}`, children: "Replace" }), aspectRatio !== undefined && (_jsx(Button, { variant: "light", size: "xs", onClick: () => setCropRequest({ kind: 'existing' }), "data-testid": `image-field-crop-${dataCanopyField}`, children: "Crop" })), _jsx(Button, { variant: "subtle", color: "red", size: "xs", onClick: handleRemove, "data-testid": `image-field-remove-${dataCanopyField}`, children: "Remove" })] }), errors?.crop && (_jsx(Text, { size: "xs", c: "red", children: errors.crop }))] })), _jsx(MediaLibrary, { opened: pickerOpen, onClose: () => setPickerOpen(false), mode: "picker", onSelect: handleAssetReady }), aspectRatio !== undefined && (_jsx(CropStep, { opened: cropRequest !== null, onClose: () => setCropRequest(null), imageSrc: cropImageSrc, aspect: aspectRatio, initialCrop: cropInitial, onConfirm: handleCropConfirm }))] }));
|
|
98
|
+
};
|
|
99
|
+
export default ImageField;
|
|
100
|
+
//# sourceMappingURL=ImageField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageField.js","sourceRoot":"","sources":["../../../src/editor/fields/ImageField.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownField.d.ts","sourceRoot":"","sources":["../../../src/editor/fields/MarkdownField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0D,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"MarkdownField.d.ts","sourceRoot":"","sources":["../../../src/editor/fields/MarkdownField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0D,MAAM,OAAO,CAAA;AAU9E,MAAM,WAAW,kBAAkB;IACjC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAmLD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA+DtD,CAAA;AAED,eAAe,aAAa,CAAA"}
|
|
@@ -3,14 +3,29 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
3
3
|
import React, { Suspense, useId, useRef, useCallback, useEffect } from 'react';
|
|
4
4
|
import { Text, Textarea } from '@mantine/core';
|
|
5
5
|
import { InsertEntryLink } from './entry-link/index.js';
|
|
6
|
+
import { MdxImageDialog } from './MdxImageDialog.js';
|
|
7
|
+
import { useApiClient } from '../context/index.js';
|
|
8
|
+
import { uploadAsset } from '../media/upload-asset.js';
|
|
6
9
|
const MDXEditorLazy = React.lazy(async () => {
|
|
7
|
-
const [{ MDXEditor }, { headingsPlugin, listsPlugin, quotePlugin, thematicBreakPlugin, markdownShortcutPlugin, linkPlugin, linkDialogPlugin, imagePlugin, tablePlugin, toolbarPlugin, codeBlockPlugin, codeMirrorPlugin, BoldItalicUnderlineToggles, BlockTypeSelect, ListsToggle, CreateLink, InsertImage, InsertTable, InsertThematicBreak, CodeToggle, InsertCodeBlock, UndoRedo, Separator, insertMarkdown$, usePublisher, },] = await Promise.all([import('@mdxeditor/editor'), import('@mdxeditor/editor')]);
|
|
10
|
+
const [{ MDXEditor }, { headingsPlugin, listsPlugin, quotePlugin, thematicBreakPlugin, markdownShortcutPlugin, linkPlugin, linkDialogPlugin, imagePlugin, tablePlugin, toolbarPlugin, codeBlockPlugin, codeMirrorPlugin, BoldItalicUnderlineToggles, BlockTypeSelect, ListsToggle, CreateLink, InsertImage, InsertTable, InsertThematicBreak, CodeToggle, InsertCodeBlock, UndoRedo, Separator, insertMarkdown$, usePublisher, useCellValues, saveImage$, closeImageDialog$, imageDialogState$, },] = await Promise.all([import('@mdxeditor/editor'), import('@mdxeditor/editor')]);
|
|
8
11
|
/** Toolbar wrapper that provides insertMarkdown to InsertEntryLink */
|
|
9
12
|
const EntryLinkToolbarButton = () => {
|
|
10
13
|
const insertMarkdown = usePublisher(insertMarkdown$);
|
|
11
14
|
return _jsx(InsertEntryLink, { onInsert: insertMarkdown });
|
|
12
15
|
};
|
|
13
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Bridges mdxeditor's realm cells (only reachable once this lazy chunk has
|
|
18
|
+
* loaded) into MdxImageDialog's plain-props interface, so that component
|
|
19
|
+
* itself never imports `@mdxeditor/editor` at runtime. Same pattern as
|
|
20
|
+
* `EntryLinkToolbarButton` above.
|
|
21
|
+
*/
|
|
22
|
+
const MdxImageDialogBridge = () => {
|
|
23
|
+
const [state] = useCellValues(imageDialogState$);
|
|
24
|
+
const saveImage = usePublisher(saveImage$);
|
|
25
|
+
const closeImageDialog = usePublisher(closeImageDialog$);
|
|
26
|
+
return _jsx(MdxImageDialog, { state: state, onSave: saveImage, onClose: closeImageDialog });
|
|
27
|
+
};
|
|
28
|
+
const WrappedEditor = ({ markdown, onChange, editorRef, imageUploadHandler }) => {
|
|
14
29
|
return (_jsx(MDXEditor, { ref: editorRef, markdown: markdown, onChange: onChange, plugins: [
|
|
15
30
|
headingsPlugin(),
|
|
16
31
|
listsPlugin(),
|
|
@@ -19,7 +34,7 @@ const MDXEditorLazy = React.lazy(async () => {
|
|
|
19
34
|
markdownShortcutPlugin(),
|
|
20
35
|
linkPlugin(),
|
|
21
36
|
linkDialogPlugin(),
|
|
22
|
-
imagePlugin(),
|
|
37
|
+
imagePlugin({ imageUploadHandler, ImageDialog: MdxImageDialogBridge }),
|
|
23
38
|
tablePlugin(),
|
|
24
39
|
codeBlockPlugin({ defaultCodeBlockLanguage: '' }),
|
|
25
40
|
codeMirrorPlugin({
|
|
@@ -86,6 +101,17 @@ export const MarkdownField = ({ id, label, value, onChange, dataCanopyField, })
|
|
|
86
101
|
const inputId = id ?? generatedId;
|
|
87
102
|
const editorRef = useRef(null);
|
|
88
103
|
const lastExternalValue = useRef(value);
|
|
104
|
+
const apiClient = useApiClient();
|
|
105
|
+
// Drives both MDXEditor's own drag/drop/paste upload flow and the Upload
|
|
106
|
+
// tab in our custom image dialog (MdxImageDialog) via the SAME presign/
|
|
107
|
+
// finalize-or-proxied pipeline used everywhere else in the editor (see
|
|
108
|
+
// media/upload-asset.ts). Stable across renders (memoized on the API
|
|
109
|
+
// client, which ApiClientProvider itself memoizes) so MDXEditor's plugin
|
|
110
|
+
// list doesn't churn on every keystroke.
|
|
111
|
+
const imageUploadHandler = useCallback(async (file) => {
|
|
112
|
+
const asset = await uploadAsset(apiClient, file);
|
|
113
|
+
return asset.src;
|
|
114
|
+
}, [apiClient]);
|
|
89
115
|
// Sync external value changes into the editor (e.g., undo, reset, load)
|
|
90
116
|
useEffect(() => {
|
|
91
117
|
if (value !== lastExternalValue.current && editorRef.current) {
|
|
@@ -97,7 +123,7 @@ export const MarkdownField = ({ id, label, value, onChange, dataCanopyField, })
|
|
|
97
123
|
lastExternalValue.current = newValue;
|
|
98
124
|
onChange(newValue);
|
|
99
125
|
}, [onChange]);
|
|
100
|
-
return (_jsxs("div", { id: inputId, "data-canopy-field": dataCanopyField, className: "canopy-markdown-field", children: [label && (_jsx(Text, { size: "sm", fw: 500, mb: 4, children: label })), _jsx(EditorContentStyles, {}), _jsx("div", { style: editorWrapperStyle, children: _jsx(Suspense, { fallback: _jsx(FallbackTextarea, { value: value, onChange: onChange }), children: _jsx(MDXEditorLazy, { markdown: value, onChange: handleChange, editorRef: editorRef }) }) })] }));
|
|
126
|
+
return (_jsxs("div", { id: inputId, "data-canopy-field": dataCanopyField, className: "canopy-markdown-field", children: [label && (_jsx(Text, { size: "sm", fw: 500, mb: 4, children: label })), _jsx(EditorContentStyles, {}), _jsx("div", { style: editorWrapperStyle, children: _jsx(Suspense, { fallback: _jsx(FallbackTextarea, { value: value, onChange: onChange }), children: _jsx(MDXEditorLazy, { markdown: value, onChange: handleChange, editorRef: editorRef, imageUploadHandler: imageUploadHandler }) }) })] }));
|
|
101
127
|
};
|
|
102
128
|
export default MarkdownField;
|
|
103
129
|
//# sourceMappingURL=MarkdownField.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownField.js","sourceRoot":"","sources":["../../../src/editor/fields/MarkdownField.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAE9E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAG9C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"MarkdownField.js","sourceRoot":"","sources":["../../../src/editor/fields/MarkdownField.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { EditingImageDialogState, InactiveImageDialogState, NewImageDialogState, SaveImageParameters } from '@mdxeditor/editor';
|
|
3
|
+
export type MdxImageDialogState = InactiveImageDialogState | NewImageDialogState | EditingImageDialogState;
|
|
4
|
+
export interface MdxImageDialogProps {
|
|
5
|
+
state: MdxImageDialogState;
|
|
6
|
+
onSave: (params: SaveImageParameters) => void;
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Custom MDXEditor image dialog (Upload / From library / By URL), replacing
|
|
11
|
+
* the stock upload-or-URL dialog. Wired in via `imagePlugin({ ImageDialog })`
|
|
12
|
+
* in MarkdownField.tsx.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MdxImageDialog: React.FC<MdxImageDialogProps>;
|
|
15
|
+
export default MdxImageDialog;
|
|
16
|
+
//# sourceMappingURL=MdxImageDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MdxImageDialog.d.ts","sourceRoot":"","sources":["../../../src/editor/fields/MdxImageDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAalD,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,mBAAmB,CAAA;AAO1B,MAAM,MAAM,mBAAmB,GAC3B,wBAAwB,GACxB,mBAAmB,GACnB,uBAAuB,CAAA;AAE3B,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,mBAAmB,CAAA;IAC1B,MAAM,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC7C,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAMD;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA+JxD,CAAA;AAED,eAAe,cAAc,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { Alert, Button, Group, Modal, Progress, Stack, Tabs, Text, TextInput } from '@mantine/core';
|
|
5
|
+
import { Dropzone } from '@mantine/dropzone';
|
|
6
|
+
import { IconAlertCircle, IconUpload } from '@tabler/icons-react';
|
|
7
|
+
import { MediaLibraryBody } from '../media/MediaLibraryBody.js';
|
|
8
|
+
import { useAssetUpload } from '../media/useAssetUpload.js';
|
|
9
|
+
import { ACCEPTED_IMAGE_MIME_TYPES, MAX_UPLOAD_BYTES } from '../media/upload-constants.js';
|
|
10
|
+
const LIBRARY_PANEL_HEIGHT = 360;
|
|
11
|
+
/**
|
|
12
|
+
* Custom MDXEditor image dialog (Upload / From library / By URL), replacing
|
|
13
|
+
* the stock upload-or-URL dialog. Wired in via `imagePlugin({ ImageDialog })`
|
|
14
|
+
* in MarkdownField.tsx.
|
|
15
|
+
*/
|
|
16
|
+
export const MdxImageDialog = ({ state, onSave, onClose }) => {
|
|
17
|
+
const [tab, setTab] = useState('upload');
|
|
18
|
+
const [altText, setAltText] = useState('');
|
|
19
|
+
const [urlValue, setUrlValue] = useState('');
|
|
20
|
+
const [dropError, setDropError] = useState(null);
|
|
21
|
+
const upload = useAssetUpload();
|
|
22
|
+
const opened = state.type !== 'inactive';
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (state.type === 'editing') {
|
|
25
|
+
setAltText(state.initialValues.altText ?? '');
|
|
26
|
+
setUrlValue(state.initialValues.src ?? '');
|
|
27
|
+
// Editing an existing image: show its current src/alt on the URL tab
|
|
28
|
+
// rather than defaulting to Upload, which would suggest replacing it.
|
|
29
|
+
setTab('url');
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
setAltText('');
|
|
33
|
+
setUrlValue('');
|
|
34
|
+
setTab('upload');
|
|
35
|
+
}
|
|
36
|
+
setDropError(null);
|
|
37
|
+
upload.reset();
|
|
38
|
+
// Reset local state whenever the dialog's target state changes identity
|
|
39
|
+
// (opened for a new insert, opened to edit a different node, or closed).
|
|
40
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
|
+
}, [state]);
|
|
42
|
+
const handleSave = (src) => {
|
|
43
|
+
onSave({ src, altText });
|
|
44
|
+
onClose();
|
|
45
|
+
};
|
|
46
|
+
const handleDrop = async (files) => {
|
|
47
|
+
const file = files[0];
|
|
48
|
+
if (!file)
|
|
49
|
+
return;
|
|
50
|
+
const asset = await upload.upload(file);
|
|
51
|
+
if (asset)
|
|
52
|
+
handleSave(asset.src);
|
|
53
|
+
};
|
|
54
|
+
const handleReject = (rejections) => {
|
|
55
|
+
setDropError(rejections[0]?.errors[0]?.message ?? 'File rejected');
|
|
56
|
+
};
|
|
57
|
+
const handlePickFromLibrary = (asset) => {
|
|
58
|
+
handleSave(asset.src);
|
|
59
|
+
};
|
|
60
|
+
const handleUrlSubmit = () => {
|
|
61
|
+
const trimmed = urlValue.trim();
|
|
62
|
+
if (!trimmed)
|
|
63
|
+
return;
|
|
64
|
+
handleSave(trimmed);
|
|
65
|
+
};
|
|
66
|
+
return (_jsx(Modal, { opened: opened, onClose: onClose, title: "Insert image", size: "lg", overlayProps: { blur: 2 }, "data-testid": "mdx-image-dialog", children: _jsxs(Stack, { gap: "sm", children: [_jsx(TextInput, { label: "Alt text", value: altText, onChange: (event) => setAltText(event.currentTarget.value), "data-testid": "mdx-image-dialog-alt" }), _jsxs(Tabs, { value: tab, onChange: (next) => {
|
|
67
|
+
if (next)
|
|
68
|
+
setTab(next);
|
|
69
|
+
}, children: [_jsxs(Tabs.List, { children: [_jsx(Tabs.Tab, { value: "upload", "data-testid": "mdx-image-dialog-tab-upload", children: "Upload" }), _jsx(Tabs.Tab, { value: "library", "data-testid": "mdx-image-dialog-tab-library", children: "From library" }), _jsx(Tabs.Tab, { value: "url", "data-testid": "mdx-image-dialog-tab-url", children: "By URL" })] }), _jsx(Tabs.Panel, { value: "upload", pt: "sm", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Dropzone, { onDrop: (files) => void handleDrop(files), onReject: handleReject, maxSize: MAX_UPLOAD_BYTES, accept: ACCEPTED_IMAGE_MIME_TYPES, loading: upload.uploading, multiple: false, "data-testid": "mdx-image-dialog-dropzone", children: _jsxs(Group, { justify: "center", gap: "xs", py: "sm", style: { pointerEvents: 'none' }, children: [_jsx(IconUpload, { size: 20 }), _jsx(Text, { size: "sm", children: "Drop an image here, or click to browse" })] }) }), upload.uploading &&
|
|
70
|
+
(upload.progress !== null ? (_jsx(Progress, { value: upload.progress * 100, size: "sm" })) : (_jsx(Progress, { value: 100, size: "sm", animated: true }))), (upload.error || dropError) && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "red", children: upload.error || dropError }))] }) }), _jsx(Tabs.Panel, { value: "library", pt: "sm", children: _jsx("div", { style: { height: LIBRARY_PANEL_HEIGHT }, children: _jsx(MediaLibraryBody, { opened: tab === 'library', mode: "picker", onSelect: handlePickFromLibrary }) }) }), _jsx(Tabs.Panel, { value: "url", pt: "sm", children: _jsxs(Stack, { gap: "xs", children: [_jsx(TextInput, { label: "Image URL", placeholder: "https://example.com/image.png", value: urlValue, onChange: (event) => setUrlValue(event.currentTarget.value), "data-testid": "mdx-image-dialog-url" }), _jsx(Group, { justify: "flex-end", children: _jsx(Button, { onClick: handleUrlSubmit, disabled: !urlValue.trim(), "data-testid": "mdx-image-dialog-url-submit", children: "Insert" }) })] }) })] }), _jsx(Group, { justify: "flex-end", children: _jsx(Button, { variant: "default", onClick: onClose, children: "Cancel" }) })] }) }));
|
|
71
|
+
};
|
|
72
|
+
export default MdxImageDialog;
|
|
73
|
+
//# sourceMappingURL=MdxImageDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MdxImageDialog.js","sourceRoot":"","sources":["../../../src/editor/fields/MdxImageDialog.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AssetRecord } from '../../api';
|
|
3
|
+
export interface AssetCardProps {
|
|
4
|
+
asset: AssetRecord;
|
|
5
|
+
/** `media.publicBaseUrl` - see AssetContext. */
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
/** Present in picker mode - clicking the card selects the asset. */
|
|
8
|
+
onSelect?: (asset: AssetRecord) => void;
|
|
9
|
+
/** Present only for admins in manage mode - renders the trash button. */
|
|
10
|
+
onDelete?: (asset: AssetRecord) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* One thumbnail card in the MediaLibrary grid. Raster and svg assets both go
|
|
14
|
+
* through `assetUrl()` unmodified - for svg (a static, non-transform src)
|
|
15
|
+
* `assetUrl` ignores the `width` option and just applies `baseUrl`, which is
|
|
16
|
+
* exactly the behavior wanted here. PDFs have no image to preview, so they
|
|
17
|
+
* render a file-type icon instead.
|
|
18
|
+
*/
|
|
19
|
+
export declare const AssetCard: React.FC<AssetCardProps>;
|
|
20
|
+
export default AssetCard;
|
|
21
|
+
//# sourceMappingURL=AssetCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetCard.d.ts","sourceRoot":"","sources":["../../../src/editor/media/AssetCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAG5C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,CAAA;IAClB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACvC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;CACxC;AAID;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA2E9C,CAAA;AAED,eAAe,SAAS,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ActionIcon, Card, Text, Tooltip } from '@mantine/core';
|
|
4
|
+
import { IconFileTypePdf, IconTrash } from '@tabler/icons-react';
|
|
5
|
+
import { assetUrl } from '../../assets/asset-url.js';
|
|
6
|
+
const THUMBNAIL_WIDTH = 160;
|
|
7
|
+
/**
|
|
8
|
+
* One thumbnail card in the MediaLibrary grid. Raster and svg assets both go
|
|
9
|
+
* through `assetUrl()` unmodified - for svg (a static, non-transform src)
|
|
10
|
+
* `assetUrl` ignores the `width` option and just applies `baseUrl`, which is
|
|
11
|
+
* exactly the behavior wanted here. PDFs have no image to preview, so they
|
|
12
|
+
* render a file-type icon instead.
|
|
13
|
+
*/
|
|
14
|
+
export const AssetCard = ({ asset, baseUrl, onSelect, onDelete }) => {
|
|
15
|
+
const uploadedDate = new Date(asset.uploadedAt).toLocaleDateString();
|
|
16
|
+
const handleKeyDown = (event) => {
|
|
17
|
+
if (!onSelect)
|
|
18
|
+
return;
|
|
19
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
onSelect(asset);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return (_jsxs(Card, { withBorder: true, radius: "md", padding: "xs", "data-testid": `asset-card-${asset.hash32}`, style: { position: 'relative', cursor: onSelect ? 'pointer' : undefined }, onClick: onSelect ? () => onSelect(asset) : undefined,
|
|
25
|
+
// Selection is otherwise a bare click handler on a non-interactive
|
|
26
|
+
// element, unreachable from the keyboard - role="button" + tabIndex
|
|
27
|
+
// makes it focusable and announced correctly, and onKeyDown mirrors
|
|
28
|
+
// native <button> Enter/Space activation.
|
|
29
|
+
role: onSelect ? 'button' : undefined, tabIndex: onSelect ? 0 : undefined, onKeyDown: onSelect ? handleKeyDown : undefined, children: [onDelete && (_jsx(ActionIcon, { variant: "filled", color: "red", size: "sm", radius: "xl", style: { position: 'absolute', top: 4, right: 4, zIndex: 1 }, "aria-label": `Delete ${asset.filename}`, "data-testid": `asset-card-delete-${asset.hash32}`, onClick: (event) => {
|
|
30
|
+
event.stopPropagation();
|
|
31
|
+
onDelete(asset);
|
|
32
|
+
}, children: _jsx(IconTrash, { size: 14 }) })), _jsx(Card.Section, { children: asset.kind === 'pdf' ? (_jsx("div", { style: {
|
|
33
|
+
height: 100,
|
|
34
|
+
display: 'flex',
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
justifyContent: 'center',
|
|
37
|
+
background: 'var(--mantine-color-gray-1)',
|
|
38
|
+
}, children: _jsx(IconFileTypePdf, { size: 40, stroke: 1.5 }) })) : (_jsx("img", { src: assetUrl(asset, { width: THUMBNAIL_WIDTH, baseUrl }), alt: asset.filename, style: { width: '100%', height: 100, objectFit: 'cover', display: 'block' } })) }), _jsx(Tooltip, { label: asset.filename, openDelay: 400, children: _jsx(Text, { size: "xs", fw: 500, truncate: true, mt: 4, children: asset.slug }) }), _jsx(Text, { size: "xs", c: "dimmed", children: uploadedDate })] }));
|
|
39
|
+
};
|
|
40
|
+
export default AssetCard;
|
|
41
|
+
//# sourceMappingURL=AssetCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetCard.js","sourceRoot":"","sources":["../../../src/editor/media/AssetCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { CropRect } from '../../assets/transform-directives';
|
|
3
|
+
export interface CropStepProps {
|
|
4
|
+
opened: boolean;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
/** Full-frame image src to crop - `assetUrl()` with no crop directive, so the user always crops the original frame. */
|
|
7
|
+
imageSrc: string;
|
|
8
|
+
/** Numeric aspect ratio (width/height) parsed from the field config's "W:H" string. */
|
|
9
|
+
aspect: number;
|
|
10
|
+
/** Existing crop rect, if re-cropping - seeds react-easy-crop's initial selection (best effort). */
|
|
11
|
+
initialCrop?: CropRect;
|
|
12
|
+
onConfirm: (rect: CropRect) => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* react-easy-crop wrapped in a Modal for the ImageField crop step. Percentage
|
|
16
|
+
* -> normalized-rect conversion is delegated to crop-math.ts's pure,
|
|
17
|
+
* independently-unit-tested `cropAreaPercentToRect`.
|
|
18
|
+
*
|
|
19
|
+
* The pan/zoom/pending-selection state lives in `CropStepBody`, mounted only
|
|
20
|
+
* while `opened` and keyed on `imageSrc` - so a fresh crop session always
|
|
21
|
+
* starts from fresh state (React remounts on key change) without an effect
|
|
22
|
+
* that would otherwise need to call `setState` synchronously to reset it.
|
|
23
|
+
*/
|
|
24
|
+
export declare const CropStep: React.FC<CropStepProps>;
|
|
25
|
+
export default CropStep;
|
|
26
|
+
//# sourceMappingURL=CropStep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CropStep.d.ts","sourceRoot":"","sources":["../../../src/editor/media/CropStep.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAKvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAA;AAGjE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,uHAAuH;IACvH,QAAQ,EAAE,MAAM,CAAA;IAChB,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAA;IACd,oGAAoG;IACpG,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,SAAS,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;CACpC;AAMD;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CA6B5C,CAAA;AAqED,eAAe,QAAQ,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Button, Group, Modal, Slider, Stack, Text } from '@mantine/core';
|
|
5
|
+
import Cropper from 'react-easy-crop';
|
|
6
|
+
import { cropAreaPercentToRect, cropRectToAreaPercent } from './crop-math.js';
|
|
7
|
+
const MIN_ZOOM = 1;
|
|
8
|
+
const MAX_ZOOM = 3;
|
|
9
|
+
const ZOOM_STEP = 0.05;
|
|
10
|
+
/**
|
|
11
|
+
* react-easy-crop wrapped in a Modal for the ImageField crop step. Percentage
|
|
12
|
+
* -> normalized-rect conversion is delegated to crop-math.ts's pure,
|
|
13
|
+
* independently-unit-tested `cropAreaPercentToRect`.
|
|
14
|
+
*
|
|
15
|
+
* The pan/zoom/pending-selection state lives in `CropStepBody`, mounted only
|
|
16
|
+
* while `opened` and keyed on `imageSrc` - so a fresh crop session always
|
|
17
|
+
* starts from fresh state (React remounts on key change) without an effect
|
|
18
|
+
* that would otherwise need to call `setState` synchronously to reset it.
|
|
19
|
+
*/
|
|
20
|
+
export const CropStep = ({ opened, onClose, imageSrc, aspect, initialCrop, onConfirm, }) => {
|
|
21
|
+
return (_jsx(Modal, { opened: opened, onClose: onClose, title: "Crop image", size: "lg", overlayProps: { blur: 2 }, "data-testid": "crop-step-modal", children: opened && (_jsx(CropStepBody, { imageSrc: imageSrc, aspect: aspect, initialCrop: initialCrop, onConfirm: onConfirm, onClose: onClose }, imageSrc)) }));
|
|
22
|
+
};
|
|
23
|
+
const CropStepBody = ({ imageSrc, aspect, initialCrop, onConfirm, onClose, }) => {
|
|
24
|
+
const [crop, setCrop] = useState({ x: 0, y: 0 });
|
|
25
|
+
const [zoom, setZoom] = useState(1);
|
|
26
|
+
const [pendingArea, setPendingArea] = useState(null);
|
|
27
|
+
const handleConfirm = () => {
|
|
28
|
+
if (!pendingArea)
|
|
29
|
+
return;
|
|
30
|
+
const rect = cropAreaPercentToRect(pendingArea);
|
|
31
|
+
if (rect)
|
|
32
|
+
onConfirm(rect);
|
|
33
|
+
};
|
|
34
|
+
const initialCroppedAreaPercentages = initialCrop ? cropRectToAreaPercent(initialCrop) : undefined;
|
|
35
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsx("div", { style: { position: 'relative', width: '100%', height: 360, background: '#000' }, children: _jsx(Cropper, { image: imageSrc, crop: crop, zoom: zoom, aspect: aspect, initialCroppedAreaPercentages: initialCroppedAreaPercentages, onCropChange: setCrop, onZoomChange: setZoom, onCropComplete: (croppedArea) => setPendingArea(croppedArea) }) }), _jsxs(Group, { gap: "sm", align: "center", children: [_jsx(Text, { size: "sm", w: 50, children: "Zoom" }), _jsx(Slider, { style: { flex: 1 }, min: MIN_ZOOM, max: MAX_ZOOM, step: ZOOM_STEP, value: zoom, onChange: setZoom, label: (value) => `${value.toFixed(2)}x` })] }), _jsxs(Group, { justify: "flex-end", children: [_jsx(Button, { variant: "default", onClick: onClose, children: "Cancel" }), _jsx(Button, { onClick: handleConfirm, disabled: !pendingArea, "data-testid": "crop-step-apply", children: "Apply crop" })] })] }));
|
|
36
|
+
};
|
|
37
|
+
export default CropStep;
|
|
38
|
+
//# sourceMappingURL=CropStep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CropStep.js","sourceRoot":"","sources":["../../../src/editor/media/CropStep.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AssetRecord } from '../../api';
|
|
3
|
+
export interface MediaLibraryProps {
|
|
4
|
+
opened: boolean;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
/**
|
|
7
|
+
* 'manage': right-side Drawer opened from the EditorSidebar Settings menu.
|
|
8
|
+
* 'picker': Modal used by ImageField ("Browse library"/"Replace") and the
|
|
9
|
+
* MDX image dialog's "From library" tab.
|
|
10
|
+
*/
|
|
11
|
+
mode: 'manage' | 'picker';
|
|
12
|
+
/** Required in picker mode - called with the chosen asset when a card is clicked. */
|
|
13
|
+
onSelect?: (asset: AssetRecord) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* One component, two presentations, sharing the same grid/upload/filter/
|
|
17
|
+
* pagination/delete core (MediaLibraryBody) - see
|
|
18
|
+
* .claude/future-tasks/assets-media-system.md "MediaManager design".
|
|
19
|
+
*/
|
|
20
|
+
export declare const MediaLibrary: React.FC<MediaLibraryProps>;
|
|
21
|
+
export default MediaLibrary;
|
|
22
|
+
//# sourceMappingURL=MediaLibrary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaLibrary.d.ts","sourceRoot":"","sources":["../../../src/editor/media/MediaLibrary.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAG5C,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB;;;;OAIG;IACH,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAA;IACzB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;CACxC;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqCpD,CAAA;AAED,eAAe,YAAY,CAAA"}
|