fimo 0.11.1-staging.1 → 0.12.0-staging.2
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/assets/content-templates/hooks-template.eta +8 -8
- package/assets/content-templates/next-client-template.eta +102 -0
- package/assets/content-templates/next-server-template.eta +75 -0
- package/assets/content-templates/types-template.eta +2 -2
- package/assets/skills/fimo-cli/SKILL.md +2 -0
- package/assets/skills/fimo-cli/references/deploy.md +22 -1
- package/assets/skills/fimo-cli/references/preview.md +4 -3
- package/assets/skills/fimo-code/references/setup-plain-vite.md +4 -2
- package/assets/skills/fimo-code/references/setup-react-router.md +4 -2
- package/assets/skills/fimo-studio/SKILL.md +3 -1
- package/assets/skills/fimo-studio/references/ai-chat.md +1 -1
- package/assets/skills/fimo-studio/references/command-palette.md +31 -0
- package/dist/cli/bundle.json +1 -1
- package/dist/cli/index.js +50 -48
- package/dist/runtime/content/client/collection-core.d.ts +37 -0
- package/dist/runtime/content/client/collection-core.js +149 -0
- package/dist/runtime/content/client/createContentClient.d.ts +3 -16
- package/dist/runtime/content/client/createContentClient.js +4 -321
- package/dist/runtime/content/client/createSingletonClient.js +3 -108
- package/dist/runtime/content/client/internal.d.ts +1 -3
- package/dist/runtime/content/client/internal.js +3 -21
- package/dist/runtime/content/client/react-content-client.d.ts +10 -0
- package/dist/runtime/content/client/react-content-client.js +165 -0
- package/dist/runtime/content/client/react-singleton-client.d.ts +7 -0
- package/dist/runtime/content/client/react-singleton-client.js +38 -0
- package/dist/runtime/content/client/runtime.d.ts +8 -0
- package/dist/runtime/content/client/runtime.js +7 -0
- package/dist/runtime/content/client/server-content-client.d.ts +9 -0
- package/dist/runtime/content/client/server-content-client.js +35 -0
- package/dist/runtime/content/client/singleton-core.d.ts +26 -0
- package/dist/runtime/content/client/singleton-core.js +73 -0
- package/dist/runtime/content/client/useQueryLocale.d.ts +1 -1
- package/dist/runtime/content/client/useQueryLocale.js +2 -3
- package/dist/runtime/content/client/vite-runtime.d.ts +3 -0
- package/dist/runtime/content/client/vite-runtime.js +23 -0
- package/dist/runtime/content/index.d.ts +16 -0
- package/dist/runtime/content/index.js +10 -0
- package/dist/runtime/content/serialization.d.ts +22 -0
- package/dist/runtime/content/serialization.js +42 -0
- package/dist/runtime/next/client-runtime.d.ts +3 -0
- package/dist/runtime/next/client-runtime.js +20 -0
- package/dist/runtime/next/client.d.ts +23 -0
- package/dist/runtime/next/client.js +22 -0
- package/dist/runtime/next/primitives/Text.d.ts +10 -0
- package/dist/runtime/next/primitives/Text.js +18 -0
- package/dist/runtime/next/primitives/server.d.ts +9 -0
- package/dist/runtime/next/primitives/server.js +46 -0
- package/dist/runtime/next/provider.d.ts +10 -0
- package/dist/runtime/next/provider.js +12 -0
- package/dist/runtime/next/server-runtime.d.ts +3 -0
- package/dist/runtime/next/server-runtime.js +20 -0
- package/dist/runtime/next/server.d.ts +13 -0
- package/dist/runtime/next/server.js +12 -0
- package/dist/runtime/primitives/index.d.ts +10 -10
- package/dist/runtime/primitives/index.js +10 -10
- package/dist/runtime/primitives/lib/image-optimization.d.ts +10 -1
- package/dist/runtime/primitives/lib/image-optimization.js +12 -12
- package/dist/runtime/primitives/lib/index.d.ts +0 -1
- package/dist/runtime/primitives/lib/index.js +0 -1
- package/dist/runtime/primitives/lib/query.d.ts +1 -1
- package/dist/runtime/react/primitives/contracts.d.ts +56 -0
- package/dist/runtime/react/primitives/contracts.js +1 -0
- package/dist/runtime/react/primitives/editable/Boolean.d.ts +5 -0
- package/dist/runtime/react/primitives/editable/Boolean.js +11 -0
- package/dist/runtime/react/primitives/editable/Date.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/Date.js +12 -0
- package/dist/runtime/react/primitives/editable/Image.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/Image.js +10 -0
- package/dist/runtime/react/primitives/editable/Json.d.ts +5 -0
- package/dist/runtime/react/primitives/editable/Json.js +11 -0
- package/dist/runtime/react/primitives/editable/RichText.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/RichText.js +11 -0
- package/dist/runtime/{primitives/components → react/primitives/editable}/Text.d.ts +3 -7
- package/dist/runtime/{primitives/components → react/primitives/editable}/Text.js +2 -1
- package/dist/runtime/react/primitives/editable/Video.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/Video.js +10 -0
- package/dist/runtime/react/primitives/renderers/DateRenderer.d.ts +11 -0
- package/dist/runtime/react/primitives/renderers/DateRenderer.js +14 -0
- package/dist/runtime/react/primitives/renderers/ImageRenderer.d.ts +8 -0
- package/dist/runtime/react/primitives/renderers/ImageRenderer.js +50 -0
- package/dist/runtime/react/primitives/renderers/RichTextRenderer.d.ts +11 -0
- package/dist/runtime/{primitives/components/RichText.js → react/primitives/renderers/RichTextRenderer.js} +4 -6
- package/dist/runtime/{primitives/components → react/primitives/renderers}/StaticImage.d.ts +2 -11
- package/dist/runtime/{primitives/components → react/primitives/renderers}/StaticImage.js +5 -3
- package/dist/runtime/react/primitives/renderers/VideoRenderer.d.ts +8 -0
- package/dist/runtime/react/primitives/renderers/VideoRenderer.js +10 -0
- package/dist/runtime/{primitives/lib → react}/useFimoLiveValue.d.ts +4 -6
- package/dist/runtime/{primitives/lib → react}/useFimoLiveValue.js +4 -10
- package/dist/runtime/vite/primitives/Images.d.ts +4 -0
- package/dist/runtime/vite/primitives/Images.js +12 -0
- package/dist/runtime/{primitives → vite}/translations.d.ts +1 -1
- package/dist/runtime/{primitives → vite}/translations.js +1 -1
- package/dist/scripts/sync-schemas.js +67 -16
- package/package.json +14 -1
- package/templates/react-router/package.json +1 -1
- package/dist/runtime/primitives/components/Boolean.d.ts +0 -29
- package/dist/runtime/primitives/components/Boolean.js +0 -33
- package/dist/runtime/primitives/components/Date.d.ts +0 -30
- package/dist/runtime/primitives/components/Date.js +0 -43
- package/dist/runtime/primitives/components/Image.d.ts +0 -72
- package/dist/runtime/primitives/components/Image.js +0 -108
- package/dist/runtime/primitives/components/Json.d.ts +0 -25
- package/dist/runtime/primitives/components/Json.js +0 -29
- package/dist/runtime/primitives/components/RichText.d.ts +0 -9
- package/dist/runtime/primitives/components/Video.d.ts +0 -31
- package/dist/runtime/primitives/components/Video.js +0 -34
- package/dist/runtime/primitives/components/index.d.ts +0 -6
- package/dist/runtime/primitives/components/index.js +0 -5
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { buildOptimizedUrl, clampQuality, computeImageOptimizationConfig, isOptimizableFimoUrl, resolveFit, toObjectFit, toPositiveInt, } from '../../../primitives/lib/image-optimization.js';
|
|
3
|
+
import { FIMO_SOURCE } from '../../../primitives/lib/primitives.js';
|
|
4
|
+
export function ImageRenderer({ value: media, children, quality, unoptimized = false, fit, width, height, alt: altOverride, overrideSrc, srcSet: srcSetOverride, sizes, style, hostSuffixes, elementRef, ...imgProps }) {
|
|
5
|
+
const mediaSource = media[FIMO_SOURCE];
|
|
6
|
+
if (children) {
|
|
7
|
+
return (_jsx("span", { ref: elementRef, "data-fimo-source": mediaSource, children: children(media) }));
|
|
8
|
+
}
|
|
9
|
+
const normalizedQuality = clampQuality(quality);
|
|
10
|
+
const normalizedFit = resolveFit(fit);
|
|
11
|
+
const requestedWidth = toPositiveInt(width);
|
|
12
|
+
const requestedHeight = toPositiveInt(height);
|
|
13
|
+
const displayWidth = requestedWidth ?? media.width;
|
|
14
|
+
const displayHeight = requestedHeight ?? media.height;
|
|
15
|
+
const originalSrc = media.url;
|
|
16
|
+
const resolvedAlt = altOverride ?? media.alt;
|
|
17
|
+
const hasSizesAttr = typeof sizes === 'string' && sizes.trim().length > 0;
|
|
18
|
+
const hasSizingHint = hasSizesAttr || requestedWidth !== undefined || requestedHeight !== undefined;
|
|
19
|
+
const canOptimizeSource = !unoptimized && isOptimizableFimoUrl(originalSrc, hostSuffixes);
|
|
20
|
+
const shouldOptimize = canOptimizeSource && hasSizingHint;
|
|
21
|
+
const { generatedSrcSet, transformWidth, transformDpr } = computeImageOptimizationConfig({
|
|
22
|
+
shouldOptimize,
|
|
23
|
+
hasSizesAttr,
|
|
24
|
+
srcSetOverride,
|
|
25
|
+
originalSrc,
|
|
26
|
+
mediaWidth: media.width,
|
|
27
|
+
renderWidth: displayWidth,
|
|
28
|
+
quality: normalizedQuality,
|
|
29
|
+
fit: normalizedFit,
|
|
30
|
+
hostSuffixes,
|
|
31
|
+
});
|
|
32
|
+
const finalSrcSet = srcSetOverride ?? generatedSrcSet;
|
|
33
|
+
const optimizedSrc = shouldOptimize
|
|
34
|
+
? buildOptimizedUrl(originalSrc, {
|
|
35
|
+
width: transformWidth,
|
|
36
|
+
quality: normalizedQuality,
|
|
37
|
+
fit: normalizedFit,
|
|
38
|
+
dpr: transformDpr,
|
|
39
|
+
hostSuffixes,
|
|
40
|
+
})
|
|
41
|
+
: originalSrc;
|
|
42
|
+
const finalSrc = overrideSrc ?? optimizedSrc;
|
|
43
|
+
const finalStyle = style?.objectFit
|
|
44
|
+
? style
|
|
45
|
+
: {
|
|
46
|
+
...style,
|
|
47
|
+
objectFit: toObjectFit(normalizedFit),
|
|
48
|
+
};
|
|
49
|
+
return (_jsx("img", { ref: elementRef, src: finalSrc, alt: resolvedAlt, width: displayWidth, height: displayHeight, style: finalStyle, sizes: sizes, srcSet: finalSrcSet, ...imgProps, "data-fimo-source": mediaSource }));
|
|
50
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef, type Ref } from 'react';
|
|
2
|
+
import type { FimoRichTextComponents, FimoRichTextContent } from '../../../primitives/lib/index.js';
|
|
3
|
+
interface RichTextRendererProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
4
|
+
document: FimoRichTextContent | null | undefined;
|
|
5
|
+
source: string | undefined;
|
|
6
|
+
components?: FimoRichTextComponents;
|
|
7
|
+
elementRef?: Ref<HTMLDivElement>;
|
|
8
|
+
}
|
|
9
|
+
export declare function RichTextRenderer({ document, source, components, elementRef, ...props }: RichTextRendererProps): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=RichTextRenderer.d.ts.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Fragment } from 'react';
|
|
3
|
-
import { FIMO_SOURCE, useFimoLiveValue, } from '../lib/index.js';
|
|
4
3
|
function DefaultParagraph({ children }) {
|
|
5
4
|
return _jsx("p", { children: children });
|
|
6
5
|
}
|
|
@@ -118,7 +117,7 @@ function renderNode(node, components, key) {
|
|
|
118
117
|
}
|
|
119
118
|
return _jsx(Fragment, { children: result }, key);
|
|
120
119
|
}
|
|
121
|
-
const children = node.content?.map((child,
|
|
120
|
+
const children = node.content?.map((child, index) => renderNode(child, components, index)) ?? null;
|
|
122
121
|
if (node.type === 'doc') {
|
|
123
122
|
return _jsx(Fragment, { children: children }, key);
|
|
124
123
|
}
|
|
@@ -128,10 +127,9 @@ function renderNode(node, components, key) {
|
|
|
128
127
|
}
|
|
129
128
|
return _jsx(Fragment, { children: children }, key);
|
|
130
129
|
}
|
|
131
|
-
export function
|
|
132
|
-
|
|
133
|
-
if (!value || !liveContent) {
|
|
130
|
+
export function RichTextRenderer({ document, source, components, elementRef, ...props }) {
|
|
131
|
+
if (!document) {
|
|
134
132
|
return null;
|
|
135
133
|
}
|
|
136
|
-
return (_jsx("div", { ref:
|
|
134
|
+
return (_jsx("div", { ref: elementRef, ...props, "data-fimo-source": source, "data-fimo-richtext": "", children: renderNode(document, components, 0) }));
|
|
137
135
|
}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
type StaticImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'children'> & {
|
|
4
|
-
/** Image quality (1-100). Defaults to 85. */
|
|
5
|
-
quality?: number;
|
|
6
|
-
/** Resize fit mode. Also maps to CSS object-fit. Defaults to 'scale-down'. */
|
|
7
|
-
fit?: ImageFit;
|
|
8
|
-
/** Skip optimization and use original URL. */
|
|
9
|
-
unoptimized?: boolean;
|
|
10
|
-
};
|
|
1
|
+
import type { StaticImageProps } from '../contracts.js';
|
|
2
|
+
export type { StaticImageProps } from '../contracts.js';
|
|
11
3
|
/**
|
|
12
4
|
* Optimized image component for non-schema images.
|
|
13
5
|
*
|
|
@@ -40,5 +32,4 @@ type StaticImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'children'> & {
|
|
|
40
32
|
* <StaticImage src="/images/logo.svg" alt="Logo" unoptimized />
|
|
41
33
|
*/
|
|
42
34
|
export declare function StaticImage(props: StaticImageProps): import("react/jsx-runtime").JSX.Element | null;
|
|
43
|
-
export {};
|
|
44
35
|
//# sourceMappingURL=StaticImage.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { clampQuality, resolveFit, toObjectFit, toPositiveInt, isOptimizableFimoUrl, buildOptimizedUrl, computeImageOptimizationConfig, } from '
|
|
2
|
+
import { clampQuality, resolveFit, toObjectFit, toPositiveInt, isOptimizableFimoUrl, buildOptimizedUrl, computeImageOptimizationConfig, } from '../../../primitives/lib/image-optimization.js';
|
|
3
3
|
/**
|
|
4
4
|
* Optimized image component for non-schema images.
|
|
5
5
|
*
|
|
@@ -32,7 +32,7 @@ import { clampQuality, resolveFit, toObjectFit, toPositiveInt, isOptimizableFimo
|
|
|
32
32
|
* <StaticImage src="/images/logo.svg" alt="Logo" unoptimized />
|
|
33
33
|
*/
|
|
34
34
|
export function StaticImage(props) {
|
|
35
|
-
const { src, alt, quality, unoptimized = false, fit, width, height, srcSet: srcSetOverride, sizes, style, ...imgProps } = props;
|
|
35
|
+
const { src, alt, quality, unoptimized = false, fit, width, height, srcSet: srcSetOverride, sizes, style, hostSuffixes, ...imgProps } = props;
|
|
36
36
|
if (!src) {
|
|
37
37
|
return null;
|
|
38
38
|
}
|
|
@@ -42,7 +42,7 @@ export function StaticImage(props) {
|
|
|
42
42
|
const requestedHeight = toPositiveInt(height);
|
|
43
43
|
const hasSizesAttr = typeof sizes === 'string' && sizes.trim().length > 0;
|
|
44
44
|
const hasSizingHint = hasSizesAttr || requestedWidth !== undefined || requestedHeight !== undefined;
|
|
45
|
-
const canOptimizeSource = !unoptimized && isOptimizableFimoUrl(src);
|
|
45
|
+
const canOptimizeSource = !unoptimized && isOptimizableFimoUrl(src, hostSuffixes);
|
|
46
46
|
const shouldOptimize = canOptimizeSource && hasSizingHint;
|
|
47
47
|
const { generatedSrcSet, transformWidth, transformDpr } = computeImageOptimizationConfig({
|
|
48
48
|
shouldOptimize,
|
|
@@ -53,6 +53,7 @@ export function StaticImage(props) {
|
|
|
53
53
|
renderWidth: requestedWidth,
|
|
54
54
|
quality: normalizedQuality,
|
|
55
55
|
fit: normalizedFit,
|
|
56
|
+
hostSuffixes,
|
|
56
57
|
});
|
|
57
58
|
const finalSrcSet = srcSetOverride ?? generatedSrcSet;
|
|
58
59
|
const optimizedSrc = shouldOptimize
|
|
@@ -61,6 +62,7 @@ export function StaticImage(props) {
|
|
|
61
62
|
quality: normalizedQuality,
|
|
62
63
|
fit: normalizedFit,
|
|
63
64
|
dpr: transformDpr,
|
|
65
|
+
hostSuffixes,
|
|
64
66
|
})
|
|
65
67
|
: src;
|
|
66
68
|
const finalStyle = style?.objectFit
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type { VideoProps } from '../contracts.js';
|
|
3
|
+
interface VideoRendererProps extends VideoProps {
|
|
4
|
+
elementRef?: RefObject<HTMLElement | null>;
|
|
5
|
+
}
|
|
6
|
+
export declare function VideoRenderer({ value: media, children, overrideSrc, autoPlay, loop, muted, playsInline, style, elementRef, ...videoProps }: VideoRendererProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=VideoRenderer.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FIMO_SOURCE } from '../../../primitives/lib/primitives.js';
|
|
3
|
+
export function VideoRenderer({ value: media, children, overrideSrc, autoPlay = true, loop = true, muted = true, playsInline = true, style, elementRef, ...videoProps }) {
|
|
4
|
+
const mediaSource = media[FIMO_SOURCE];
|
|
5
|
+
if (children) {
|
|
6
|
+
return (_jsx("span", { ref: elementRef, "data-fimo-source": mediaSource, children: children(media) }));
|
|
7
|
+
}
|
|
8
|
+
const finalSrc = overrideSrc ?? media.url;
|
|
9
|
+
return (_jsx("video", { ref: elementRef, src: finalSrc, width: media.width, height: media.height, autoPlay: autoPlay, loop: loop, muted: muted, playsInline: playsInline, style: style, ...videoProps, "data-fimo-source": mediaSource }));
|
|
10
|
+
}
|
|
@@ -9,14 +9,12 @@ import { type RefObject } from 'react';
|
|
|
9
9
|
* preview knows to dispatch events instead of falling back to raw DOM writes,
|
|
10
10
|
* and updates internal state when a mutation arrives.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* The hook is framework-adapter-neutral: Vite and Next.js clients render the
|
|
13
|
+
* same React primitives, and environments that never dispatch
|
|
14
|
+
* `fimo:live-edit` simply keep mirroring the external value.
|
|
15
15
|
*/
|
|
16
|
-
declare function
|
|
16
|
+
export declare function useFimoLiveValue<T, E extends HTMLElement = HTMLElement>(external: T): {
|
|
17
17
|
value: T;
|
|
18
18
|
ref: RefObject<E | null>;
|
|
19
19
|
};
|
|
20
|
-
export declare const useFimoLiveValue: typeof useFimoLiveValueDev;
|
|
21
|
-
export {};
|
|
22
20
|
//# sourceMappingURL=useFimoLiveValue.d.ts.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
2
1
|
import { useEffect, useRef, useState } from 'react';
|
|
3
2
|
/**
|
|
4
3
|
* useFimoLiveValue — mirror a Fimo value into React state so live edits
|
|
@@ -10,11 +9,11 @@ import { useEffect, useRef, useState } from 'react';
|
|
|
10
9
|
* preview knows to dispatch events instead of falling back to raw DOM writes,
|
|
11
10
|
* and updates internal state when a mutation arrives.
|
|
12
11
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* The hook is framework-adapter-neutral: Vite and Next.js clients render the
|
|
13
|
+
* same React primitives, and environments that never dispatch
|
|
14
|
+
* `fimo:live-edit` simply keep mirroring the external value.
|
|
16
15
|
*/
|
|
17
|
-
function
|
|
16
|
+
export function useFimoLiveValue(external) {
|
|
18
17
|
const ref = useRef(null);
|
|
19
18
|
const [value, setValue] = useState(external);
|
|
20
19
|
const externalRef = useRef(external);
|
|
@@ -42,8 +41,3 @@ function useFimoLiveValueDev(external) {
|
|
|
42
41
|
}, []);
|
|
43
42
|
return { value, ref };
|
|
44
43
|
}
|
|
45
|
-
function useFimoLiveValueProd(external) {
|
|
46
|
-
const ref = useRef(null);
|
|
47
|
-
return { value: external, ref };
|
|
48
|
-
}
|
|
49
|
-
export const useFimoLiveValue = (import.meta.env.DEV ? useFimoLiveValueDev : useFimoLiveValueProd);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ImageProps, StaticImageProps } from '../../react/primitives/contracts.js';
|
|
2
|
+
export declare function Image(props: ImageProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare function StaticImage(props: StaticImageProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
//# sourceMappingURL=Images.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { parseImageHostSuffixes } from '../../primitives/lib/image-optimization.js';
|
|
3
|
+
import { Image as BaseImage } from '../../react/primitives/editable/Image.js';
|
|
4
|
+
import { StaticImage as BaseStaticImage } from '../../react/primitives/renderers/StaticImage.js';
|
|
5
|
+
const viteEnvironment = import.meta.env;
|
|
6
|
+
const IMAGE_HOST_SUFFIXES = parseImageHostSuffixes(viteEnvironment?.VITE_FIMO_IMAGE_HOSTS);
|
|
7
|
+
export function Image(props) {
|
|
8
|
+
return _jsx(BaseImage, { ...props, hostSuffixes: IMAGE_HOST_SUFFIXES });
|
|
9
|
+
}
|
|
10
|
+
export function StaticImage(props) {
|
|
11
|
+
return _jsx(BaseStaticImage, { ...props, hostSuffixes: IMAGE_HOST_SUFFIXES });
|
|
12
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useSyncExternalStore } from 'react';
|
|
2
2
|
import { defaultLocale as bundledDefaultLocale, dictionaries as bundledDictionaries, signature as translationSignature, updatedAt as bundledUpdatedAt, } from 'virtual:translations';
|
|
3
3
|
import { useLocale } from '../i18n/FimoLocaleContext.js';
|
|
4
|
-
import { FimoString } from '
|
|
4
|
+
import { FimoString } from '../primitives/lib/index.js';
|
|
5
5
|
const labelListeners = new Set();
|
|
6
6
|
let labelState = {
|
|
7
7
|
dictionaries: bundledDictionaries ?? {},
|
|
@@ -5,8 +5,8 @@ import { basename, dirname, join, resolve } from 'node:path';
|
|
|
5
5
|
* hook files generated from the hooks template.
|
|
6
6
|
*
|
|
7
7
|
* - Scans `<cwd>/src/schemas/*.json`.
|
|
8
|
-
* - Renders
|
|
9
|
-
* - Creates/updates `<
|
|
8
|
+
* - Renders the shared types plus either the Vite facade or the split Next server/client facades.
|
|
9
|
+
* - Creates/updates `<Pascal>.types.ts` and `<Pascal>.ts`, plus `<Pascal>.client.ts` for Next; deletes orphans.
|
|
10
10
|
*/
|
|
11
11
|
import { pascalCase } from 'change-case';
|
|
12
12
|
import { Eta } from 'eta';
|
|
@@ -81,6 +81,32 @@ function resolveProjectRuntime(cwd) {
|
|
|
81
81
|
}
|
|
82
82
|
return { packageRoot: dirname(packagePath), version };
|
|
83
83
|
}
|
|
84
|
+
function resolveContentRuntimeImport(cwd) {
|
|
85
|
+
const packagePath = join(cwd, 'package.json');
|
|
86
|
+
if (!existsSync(packagePath)) {
|
|
87
|
+
return 'fimo/ui';
|
|
88
|
+
}
|
|
89
|
+
let packageJson;
|
|
90
|
+
try {
|
|
91
|
+
packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return 'fimo/ui';
|
|
95
|
+
}
|
|
96
|
+
if (!packageJson || typeof packageJson !== 'object' || Array.isArray(packageJson)) {
|
|
97
|
+
return 'fimo/ui';
|
|
98
|
+
}
|
|
99
|
+
const manifest = packageJson;
|
|
100
|
+
const dependencyGroups = ['dependencies', 'devDependencies', 'peerDependencies'];
|
|
101
|
+
const usesNext = dependencyGroups.some((group) => {
|
|
102
|
+
const dependencies = manifest[group];
|
|
103
|
+
return Boolean(dependencies &&
|
|
104
|
+
typeof dependencies === 'object' &&
|
|
105
|
+
!Array.isArray(dependencies) &&
|
|
106
|
+
typeof dependencies.next === 'string');
|
|
107
|
+
});
|
|
108
|
+
return usesNext ? 'fimo/next' : 'fimo/ui';
|
|
109
|
+
}
|
|
84
110
|
function templatePath(runtime, fileName) {
|
|
85
111
|
if (runtime && !isVersionAtLeast(runtime.version, MINIMUM_THIN_CONTENT_CLIENT_RUNTIME_VERSION)) {
|
|
86
112
|
return join(runtime.packageRoot, 'assets', 'content-templates', fileName);
|
|
@@ -115,11 +141,11 @@ function loadSchemas(schemaFiles) {
|
|
|
115
141
|
}
|
|
116
142
|
const candidate = parsed;
|
|
117
143
|
if (typeof candidate.uid !== 'string' || candidate.uid.length === 0) {
|
|
118
|
-
errors.push(`${basename(path)} is missing
|
|
144
|
+
errors.push(`${basename(path)} is missing "uid". Set it to "${fileUid}".`);
|
|
119
145
|
continue;
|
|
120
146
|
}
|
|
121
147
|
if (candidate.uid !== fileUid) {
|
|
122
|
-
errors.push(`${basename(path)} declares uid
|
|
148
|
+
errors.push(`${basename(path)} declares uid "${candidate.uid}". It must match the file name "${fileUid}".`);
|
|
123
149
|
continue;
|
|
124
150
|
}
|
|
125
151
|
const validation = SCHEMA_DEFINITION_SCHEMA.safeParse(candidate);
|
|
@@ -212,16 +238,41 @@ export function syncSchemas(options = {}) {
|
|
|
212
238
|
const schemas = loadSchemas(schemaFiles);
|
|
213
239
|
const generatedFilesOnDisk = getGeneratedFiles(schemasDir);
|
|
214
240
|
const runtime = schemas.length > 0 || generatedFilesOnDisk.length > 0 ? resolveProjectRuntime(cwd) : null;
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
241
|
+
const runtimeImport = resolveContentRuntimeImport(cwd);
|
|
242
|
+
const usesCurrentNextRuntime = runtimeImport === 'fimo/next' &&
|
|
243
|
+
runtime !== null &&
|
|
244
|
+
isVersionAtLeast(runtime.version, MINIMUM_THIN_CONTENT_CLIENT_RUNTIME_VERSION);
|
|
245
|
+
const templates = usesCurrentNextRuntime
|
|
246
|
+
? [
|
|
247
|
+
{
|
|
248
|
+
suffix: '.types.ts',
|
|
249
|
+
path: templatePath(runtime, 'types-template.eta'),
|
|
250
|
+
runtimeImport: 'fimo/next',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
suffix: '.ts',
|
|
254
|
+
path: templatePath(runtime, 'next-server-template.eta'),
|
|
255
|
+
runtimeImport: 'fimo/next',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
suffix: '.client.ts',
|
|
259
|
+
path: templatePath(runtime, 'next-client-template.eta'),
|
|
260
|
+
runtimeImport: 'fimo/next/client',
|
|
261
|
+
},
|
|
262
|
+
]
|
|
263
|
+
: [
|
|
264
|
+
{
|
|
265
|
+
suffix: '.types.ts',
|
|
266
|
+
path: templatePath(runtime, 'types-template.eta'),
|
|
267
|
+
runtimeImport,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
suffix: '.ts',
|
|
271
|
+
path: templatePath(runtime, 'hooks-template.eta'),
|
|
272
|
+
runtimeImport,
|
|
273
|
+
},
|
|
274
|
+
];
|
|
275
|
+
const loadedTemplates = templates.map((template) => ({
|
|
225
276
|
...template,
|
|
226
277
|
source: readFileSync(template.path, 'utf-8'),
|
|
227
278
|
}));
|
|
@@ -234,10 +285,10 @@ export function syncSchemas(options = {}) {
|
|
|
234
285
|
let deleted = 0;
|
|
235
286
|
for (const { pascal, schema } of schemas) {
|
|
236
287
|
const model = schemaToModel(schema, localizedByUid);
|
|
237
|
-
for (const template of
|
|
288
|
+
for (const template of loadedTemplates) {
|
|
238
289
|
const tsPath = join(schemasDir, `${pascal}${template.suffix}`);
|
|
239
290
|
expectedTsFiles.add(tsPath);
|
|
240
|
-
generatedFiles.set(tsPath, eta.renderString(template.source, { model }));
|
|
291
|
+
generatedFiles.set(tsPath, eta.renderString(template.source, { model, runtimeImport: template.runtimeImport }));
|
|
241
292
|
}
|
|
242
293
|
}
|
|
243
294
|
for (const [tsPath, content] of generatedFiles) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fimo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0-staging.2",
|
|
4
4
|
"description": "Fimo CLI - create, deploy, and manage Fimo projects",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fimo": "dist/cli/index.js"
|
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
"types": "./dist/runtime/index.d.ts",
|
|
23
23
|
"import": "./dist/runtime/index.js"
|
|
24
24
|
},
|
|
25
|
+
"./content": {
|
|
26
|
+
"types": "./dist/runtime/content/index.d.ts",
|
|
27
|
+
"import": "./dist/runtime/content/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./next": {
|
|
30
|
+
"types": "./dist/runtime/next/server.d.ts",
|
|
31
|
+
"import": "./dist/runtime/next/server.js"
|
|
32
|
+
},
|
|
33
|
+
"./next/client": {
|
|
34
|
+
"types": "./dist/runtime/next/client.d.ts",
|
|
35
|
+
"import": "./dist/runtime/next/client.js"
|
|
36
|
+
},
|
|
25
37
|
"./react-router": {
|
|
26
38
|
"types": "./dist/runtime/react-router/index.d.ts",
|
|
27
39
|
"import": "./dist/runtime/react-router/index.js"
|
|
@@ -63,6 +75,7 @@
|
|
|
63
75
|
"publish:dry-run": "npm publish --dry-run",
|
|
64
76
|
"postinstall": "node scripts/postinstall.mjs",
|
|
65
77
|
"test": "vitest run",
|
|
78
|
+
"test:integration:next": "vitest run --config vitest.integration.config.ts",
|
|
66
79
|
"test:watch": "vitest",
|
|
67
80
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
68
81
|
},
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { type ComponentPropsWithoutRef, type ElementType, type ReactNode } from 'react';
|
|
2
|
-
import { type FimoBoolean } from '../lib/index.js';
|
|
3
|
-
type BooleanProps<T extends ElementType = 'span'> = {
|
|
4
|
-
value: FimoBoolean;
|
|
5
|
-
as?: T;
|
|
6
|
-
children?: (value: boolean) => ReactNode;
|
|
7
|
-
} & Omit<ComponentPropsWithoutRef<T>, 'value' | 'as' | 'children'>;
|
|
8
|
-
/**
|
|
9
|
-
* Boolean component for rendering boolean values with source tracking
|
|
10
|
-
*
|
|
11
|
-
* Use children render function to customize how the boolean is displayed.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* // Default (renders "true" or "false")
|
|
15
|
-
* <Boolean value={product.inStock} />
|
|
16
|
-
*
|
|
17
|
-
* // With render function for custom display
|
|
18
|
-
* <Boolean value={product.inStock}>
|
|
19
|
-
* {(val) => val ? '✓ In Stock' : '✗ Out of Stock'}
|
|
20
|
-
* </Boolean>
|
|
21
|
-
*
|
|
22
|
-
* // Conditional styling
|
|
23
|
-
* <Boolean value={product.featured}>
|
|
24
|
-
* {(val) => val && <span className="badge">Featured</span>}
|
|
25
|
-
* </Boolean>
|
|
26
|
-
*/
|
|
27
|
-
export declare function Boolean<T extends ElementType = 'span'>({ value, as, children, ...props }: BooleanProps<T>): import("react/jsx-runtime").JSX.Element | null;
|
|
28
|
-
export {};
|
|
29
|
-
//# sourceMappingURL=Boolean.d.ts.map
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { FIMO_SOURCE, useFimoLiveValue } from '../lib/index.js';
|
|
3
|
-
/**
|
|
4
|
-
* Boolean component for rendering boolean values with source tracking
|
|
5
|
-
*
|
|
6
|
-
* Use children render function to customize how the boolean is displayed.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* // Default (renders "true" or "false")
|
|
10
|
-
* <Boolean value={product.inStock} />
|
|
11
|
-
*
|
|
12
|
-
* // With render function for custom display
|
|
13
|
-
* <Boolean value={product.inStock}>
|
|
14
|
-
* {(val) => val ? '✓ In Stock' : '✗ Out of Stock'}
|
|
15
|
-
* </Boolean>
|
|
16
|
-
*
|
|
17
|
-
* // Conditional styling
|
|
18
|
-
* <Boolean value={product.featured}>
|
|
19
|
-
* {(val) => val && <span className="badge">Featured</span>}
|
|
20
|
-
* </Boolean>
|
|
21
|
-
*/
|
|
22
|
-
export function Boolean({ value, as, children, ...props }) {
|
|
23
|
-
const Component = as || 'span';
|
|
24
|
-
const { value: liveBool, ref } = useFimoLiveValue(value ? value.valueOf() : false);
|
|
25
|
-
// Defensive: return null if no value
|
|
26
|
-
if (!value) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
const source = value[FIMO_SOURCE];
|
|
30
|
-
// Use render function if provided, otherwise display raw boolean
|
|
31
|
-
const content = children ? children(liveBool) : String(liveBool);
|
|
32
|
-
return (_jsx(Component, { ref: ref, ...props, "data-fimo-source": source, children: content }));
|
|
33
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
2
|
-
import { type FimoDate, type FimoString } from '../lib/index.js';
|
|
3
|
-
type DateProps = {
|
|
4
|
-
value: FimoDate | FimoString | Date | string;
|
|
5
|
-
children?: (date: Date) => ReactNode;
|
|
6
|
-
className?: string;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Date component for rendering dates with source tracking
|
|
10
|
-
*
|
|
11
|
-
* Default format uses browser's locale (e.g., "28 ene 2026" in Spain, "Jan 28, 2026" in US)
|
|
12
|
-
* Use children render function for custom formatting.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // Default format (locale-aware)
|
|
16
|
-
* <Date value={post.publishedAt} />
|
|
17
|
-
*
|
|
18
|
-
* // Custom format with render function
|
|
19
|
-
* <Date value={post.publishedAt}>
|
|
20
|
-
* {(date) => format(date, 'EEEE, MMMM do yyyy')}
|
|
21
|
-
* </Date>
|
|
22
|
-
*
|
|
23
|
-
* // Relative time
|
|
24
|
-
* <Date value={post.publishedAt}>
|
|
25
|
-
* {(date) => formatDistanceToNow(date, { addSuffix: true })}
|
|
26
|
-
* </Date>
|
|
27
|
-
*/
|
|
28
|
-
export declare function Date({ value, children, className }: DateProps): import("react/jsx-runtime").JSX.Element | null;
|
|
29
|
-
export {};
|
|
30
|
-
//# sourceMappingURL=Date.d.ts.map
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { FIMO_SOURCE, useFimoLiveValue } from '../lib/index.js';
|
|
3
|
-
/**
|
|
4
|
-
* Date component for rendering dates with source tracking
|
|
5
|
-
*
|
|
6
|
-
* Default format uses browser's locale (e.g., "28 ene 2026" in Spain, "Jan 28, 2026" in US)
|
|
7
|
-
* Use children render function for custom formatting.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* // Default format (locale-aware)
|
|
11
|
-
* <Date value={post.publishedAt} />
|
|
12
|
-
*
|
|
13
|
-
* // Custom format with render function
|
|
14
|
-
* <Date value={post.publishedAt}>
|
|
15
|
-
* {(date) => format(date, 'EEEE, MMMM do yyyy')}
|
|
16
|
-
* </Date>
|
|
17
|
-
*
|
|
18
|
-
* // Relative time
|
|
19
|
-
* <Date value={post.publishedAt}>
|
|
20
|
-
* {(date) => formatDistanceToNow(date, { addSuffix: true })}
|
|
21
|
-
* </Date>
|
|
22
|
-
*/
|
|
23
|
-
export function Date({ value, children, className }) {
|
|
24
|
-
const { value: liveValue, ref } = useFimoLiveValue(value ?? null);
|
|
25
|
-
// Defensive: return null if no value
|
|
26
|
-
if (!liveValue) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
const source = typeof value === 'object' && FIMO_SOURCE in value ? value[FIMO_SOURCE] : undefined;
|
|
30
|
-
const rawValue = liveValue instanceof String ? liveValue.toString() : liveValue;
|
|
31
|
-
const dateValue = new globalThis.Date(rawValue);
|
|
32
|
-
// Format for datetime attribute (ISO format)
|
|
33
|
-
const isoDate = dateValue.toISOString().split('T')[0];
|
|
34
|
-
// Render content - use children if provided, otherwise locale-aware default
|
|
35
|
-
const content = children
|
|
36
|
-
? children(dateValue)
|
|
37
|
-
: dateValue.toLocaleDateString(undefined, {
|
|
38
|
-
year: 'numeric',
|
|
39
|
-
month: 'short',
|
|
40
|
-
day: 'numeric',
|
|
41
|
-
});
|
|
42
|
-
return (_jsx("time", { ref: ref, dateTime: isoDate, className: className, "data-fimo-source": source, children: content }));
|
|
43
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
|
-
import { type ImageFit } from '../lib/image-optimization.js';
|
|
3
|
-
import { type FimoMedia } from '../lib/index.js';
|
|
4
|
-
/** Base props for Image, extending native img props with optimization options. */
|
|
5
|
-
type BaseImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'alt' | 'children'> & {
|
|
6
|
-
/** Image quality (1-100). Defaults to 85. */
|
|
7
|
-
quality?: number;
|
|
8
|
-
/** Resize fit mode. Also maps to CSS object-fit. Defaults to 'scale-down'. */
|
|
9
|
-
fit?: ImageFit;
|
|
10
|
-
/** Skip optimization and use original URL. */
|
|
11
|
-
unoptimized?: boolean;
|
|
12
|
-
overrideSrc?: string;
|
|
13
|
-
};
|
|
14
|
-
type ImageProps = BaseImageProps & {
|
|
15
|
-
/** The FimoMedia object from schema data. */
|
|
16
|
-
value: FimoMedia;
|
|
17
|
-
/** Override the alt text from FimoMedia. */
|
|
18
|
-
alt?: string;
|
|
19
|
-
/** Render function for custom markup (bypasses default img rendering). */
|
|
20
|
-
children?: (media: FimoMedia) => ReactNode;
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Image component for rendering schema media with optional optimization.
|
|
24
|
-
*
|
|
25
|
-
* Optimization:
|
|
26
|
-
* - Applies only to http(s) URLs on *.fimo.team, *.fimo.ai, and *.fimo.site.
|
|
27
|
-
* - Generates optimized `src` automatically only when at least one sizing hint
|
|
28
|
-
* is provided (`width`, `height`, or `sizes`).
|
|
29
|
-
* - Uses default quality 85 and internal format=auto.
|
|
30
|
-
* - Maps `fit` to CSS object-fit by default.
|
|
31
|
-
*
|
|
32
|
-
* Escape hatch:
|
|
33
|
-
* - Pass `unoptimized` to bypass all URL rewriting.
|
|
34
|
-
*
|
|
35
|
-
* Use children render function for custom rendering (e.g., picture element).
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* <Image value={post.coverImage} />
|
|
39
|
-
* <Image value={post.coverImage} className="rounded-lg" loading="lazy" />
|
|
40
|
-
*
|
|
41
|
-
* // With render function for custom markup
|
|
42
|
-
* <Image value={post.cover}>
|
|
43
|
-
* {(media) => (
|
|
44
|
-
* <img src={media.url} alt={media.alt} />
|
|
45
|
-
* )}
|
|
46
|
-
* </Image>
|
|
47
|
-
*
|
|
48
|
-
* // Fixed-size mode (no `sizes`): uses DPR candidates
|
|
49
|
-
* <Image value={post.coverImage} width={320} />
|
|
50
|
-
* // srcSet:
|
|
51
|
-
* // ...?w=320&q=85&fit=scale-down&f=auto&dpr=1 1x,
|
|
52
|
-
* // ...?w=320&q=85&fit=scale-down&f=auto&dpr=2 2x
|
|
53
|
-
* // src:
|
|
54
|
-
* // ...?w=320&q=85&fit=scale-down&f=auto&dpr=2
|
|
55
|
-
*
|
|
56
|
-
* // Responsive mode (`sizes`): uses width candidates
|
|
57
|
-
* <Image
|
|
58
|
-
* value={post.coverImage}
|
|
59
|
-
* sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
|
60
|
-
* />
|
|
61
|
-
* // srcSet (example):
|
|
62
|
-
* // ...?w=640&q=85&fit=scale-down&f=auto 640w,
|
|
63
|
-
* // ...?w=750&q=85&fit=scale-down&f=auto 750w,
|
|
64
|
-
* // ...?w=828&q=85&fit=scale-down&f=auto 828w
|
|
65
|
-
*
|
|
66
|
-
* // No `width`/`height`/`sizes`: keep source URL unchanged
|
|
67
|
-
* <Image value={post.coverImage} />
|
|
68
|
-
* // src: original media URL
|
|
69
|
-
*/
|
|
70
|
-
export declare function Image(props: ImageProps): import("react/jsx-runtime").JSX.Element | null;
|
|
71
|
-
export {};
|
|
72
|
-
//# sourceMappingURL=Image.d.ts.map
|