meno-core 1.0.54 → 1.1.0
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/bin/cli.ts +19 -205
- package/build-astro.ts +31 -27
- package/dist/bin/cli.js +15 -154
- package/dist/bin/cli.js.map +2 -2
- package/dist/chunks/{chunk-7HWQUVTU.js → chunk-2IIQK7T3.js} +1353 -347
- package/dist/chunks/chunk-2IIQK7T3.js.map +7 -0
- package/dist/chunks/{chunk-3XER4E5W.js → chunk-4ZRU52J2.js} +6 -5
- package/dist/chunks/{chunk-3XER4E5W.js.map → chunk-4ZRU52J2.js.map} +2 -2
- package/dist/chunks/{chunk-NVRBTSQG.js → chunk-5X4HCN7J.js} +4 -4
- package/dist/chunks/chunk-5X4HCN7J.js.map +7 -0
- package/dist/chunks/{chunk-FZITJSSS.js → chunk-7CCVOL5M.js} +14 -14
- package/dist/chunks/chunk-7CCVOL5M.js.map +7 -0
- package/dist/chunks/{chunk-HIZMY3EP.js → chunk-AR7LHIFY.js} +8 -2
- package/dist/chunks/chunk-AR7LHIFY.js.map +7 -0
- package/dist/chunks/{chunk-AE3QK5QW.js → chunk-NUP7H7D3.js} +31 -26
- package/dist/chunks/chunk-NUP7H7D3.js.map +7 -0
- package/dist/chunks/{chunk-5ETZFREW.js → chunk-QWTQZHG3.js} +186 -79
- package/dist/chunks/chunk-QWTQZHG3.js.map +7 -0
- package/dist/chunks/{chunk-STDY3OVM.js → chunk-XTKNX4FW.js} +3 -3
- package/dist/chunks/{chunk-STDY3OVM.js.map → chunk-XTKNX4FW.js.map} +2 -2
- package/dist/chunks/{fs-ZI5JEU7V.js → fs-G3ANWEFP.js} +2 -2
- package/dist/lib/client/index.js +381 -159
- package/dist/lib/client/index.js.map +3 -3
- package/dist/lib/server/index.js +14345 -6265
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +292 -87
- package/dist/lib/shared/index.js.map +4 -4
- package/dist/lib/shared/richtext/index.js +1 -1
- package/dist/lib/test-utils/index.js +27 -21
- package/dist/lib/test-utils/index.js.map +2 -2
- package/lib/client/ErrorBoundary.test.tsx +147 -90
- package/lib/client/ErrorBoundary.tsx +8 -5
- package/lib/client/componentRegistry.ts +0 -1
- package/lib/client/core/ComponentBuilder.test.ts +150 -0
- package/lib/client/core/ComponentBuilder.ts +112 -12
- package/lib/client/core/ComponentRenderer.test.tsx +1 -2
- package/lib/client/core/builders/embedBuilder.ts +1 -1
- package/lib/client/core/builders/linkBuilder.ts +2 -2
- package/lib/client/core/builders/linkNodeBuilder.ts +1 -1
- package/lib/client/core/builders/listBuilder.ts +41 -4
- package/lib/client/core/builders/localeListBuilder.ts +2 -2
- package/lib/client/core/cmsTemplateProcessor.ts +1 -2
- package/lib/client/hmr/HMRManager.tsx +39 -30
- package/lib/client/hmrCssReload.ts +35 -6
- package/lib/client/hmrWebSocket.ts +5 -5
- package/lib/client/hooks/useColorVariables.ts +12 -9
- package/lib/client/hooks/usePropertyAutocomplete.ts +1 -1
- package/lib/client/hooks/useVariables.ts +3 -1
- package/lib/client/meno-filter/MenoFilter.test.ts +28 -28
- package/lib/client/meno-filter/MenoFilter.ts +9 -6
- package/lib/client/meno-filter/bindings.ts +3 -3
- package/lib/client/meno-filter/init.ts +3 -3
- package/lib/client/meno-filter/renderer.ts +8 -4
- package/lib/client/meno-filter/ui.ts +1 -1
- package/lib/client/meno-filter/updates.ts +3 -2
- package/lib/client/meno-filter/utils.ts +1 -1
- package/lib/client/navigation.test.ts +40 -44
- package/lib/client/responsiveStyleResolver.knownTokens.test.ts +71 -0
- package/lib/client/responsiveStyleResolver.test.ts +5 -5
- package/lib/client/responsiveStyleResolver.ts +61 -4
- package/lib/client/routing/RouteLoader.test.ts +1 -1
- package/lib/client/routing/RouteLoader.ts +8 -3
- package/lib/client/routing/Router.tsx +181 -152
- package/lib/client/scripts/ScriptExecutor.ts +3 -3
- package/lib/client/services/PrefetchService.test.ts +1 -1
- package/lib/client/services/PrefetchService.ts +13 -5
- package/lib/client/styleProcessor.ts +9 -3
- package/lib/client/styles/StyleInjector.ts +1 -1
- package/lib/client/styles/UtilityClassCollector.ts +54 -13
- package/lib/client/templateEngine.test.ts +17 -23
- package/lib/client/templateEngine.ts +7 -13
- package/lib/client/theme.test.ts +1 -1
- package/lib/server/__integration__/server-lifecycle.test.ts +3 -1
- package/lib/server/__integration__/static-assets.test.ts +2 -2
- package/lib/server/__integration__/test-helpers.ts +3 -4
- package/lib/server/astro/cmsPageEmitter.ts +4 -5
- package/lib/server/astro/componentEmitter.ts +11 -8
- package/lib/server/astro/nodeToAstro.test.ts +4 -4
- package/lib/server/astro/nodeToAstro.ts +41 -36
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +11 -11
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +7 -6
- package/lib/server/astro/pageEmitter.ts +1 -1
- package/lib/server/astro/templateTransformer.ts +3 -3
- package/lib/server/createServer.ts +2 -2
- package/lib/server/cssGenerator.ts +2 -2
- package/lib/server/fileWatcher.test.ts +41 -4
- package/lib/server/fileWatcher.ts +102 -23
- package/lib/server/index.ts +7 -16
- package/lib/server/jsonLoader.test.ts +3 -3
- package/lib/server/jsonLoader.ts +8 -5
- package/lib/server/middleware/cors.test.ts +1 -1
- package/lib/server/middleware/cors.ts +2 -2
- package/lib/server/middleware/errorHandler.test.ts +2 -2
- package/lib/server/middleware/logger.test.ts +3 -3
- package/lib/server/middleware/logger.ts +7 -7
- package/lib/server/migrateTemplates.ts +3 -3
- package/lib/server/projectContext.ts +1 -1
- package/lib/server/providers/fileSystemCMSProvider.test.ts +18 -4
- package/lib/server/providers/fileSystemCMSProvider.ts +27 -18
- package/lib/server/routes/api/cms.ts +1 -1
- package/lib/server/routes/api/colors.test.ts +2 -2
- package/lib/server/routes/api/components.ts +1 -1
- package/lib/server/routes/api/config.ts +3 -3
- package/lib/server/routes/api/core-routes.ts +2 -2
- package/lib/server/routes/api/enums.test.ts +2 -2
- package/lib/server/routes/api/functions.ts +1 -1
- package/lib/server/routes/api/variables.test.ts +2 -2
- package/lib/server/routes/index.ts +2 -2
- package/lib/server/routes/pages.ts +1 -2
- package/lib/server/routes/static.ts +1 -2
- package/lib/server/runtime/bundler.ts +65 -43
- package/lib/server/runtime/fs.ts +13 -13
- package/lib/server/runtime/httpServer.ts +3 -3
- package/lib/server/services/ColorService.test.ts +3 -3
- package/lib/server/services/ColorService.ts +5 -2
- package/lib/server/services/EnumService.test.ts +2 -2
- package/lib/server/services/EnumService.ts +4 -1
- package/lib/server/services/VariableService.test.ts +7 -7
- package/lib/server/services/VariableService.ts +5 -2
- package/lib/server/services/cmsService.test.ts +44 -44
- package/lib/server/services/cmsService.ts +17 -97
- package/lib/server/services/componentService.test.ts +10 -16
- package/lib/server/services/componentService.ts +20 -12
- package/lib/server/services/configService.test.ts +13 -14
- package/lib/server/services/configService.ts +30 -0
- package/lib/server/services/fileWatcherService.ts +2 -2
- package/lib/server/services/pageService.test.ts +7 -7
- package/lib/server/services/pageService.ts +18 -5
- package/lib/server/ssr/attributeBuilder.ts +2 -2
- package/lib/server/ssr/cmsSSRProcessor.ts +1 -2
- package/lib/server/ssr/errorOverlay.ts +1 -1
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +6 -5
- package/lib/server/ssr/htmlGenerator.test.ts +0 -1
- package/lib/server/ssr/htmlGenerator.ts +7 -7
- package/lib/server/ssr/imageMetadata.ts +2 -2
- package/lib/server/ssr/jsCollector.test.ts +5 -5
- package/lib/server/ssr/liveReloadIntegration.test.ts +7 -8
- package/lib/server/ssr/ssrRenderer.branches.test.ts +9 -9
- package/lib/server/ssr/ssrRenderer.test.ts +125 -15
- package/lib/server/ssr/ssrRenderer.ts +206 -38
- package/lib/server/ssrRenderer.test.ts +6 -6
- package/lib/server/utils/jsonLineMapper.ts +7 -7
- package/lib/server/validateStyleCoverage.ts +0 -1
- package/lib/server/websocketManager.ts +12 -0
- package/lib/shared/breakpoints.test.ts +5 -5
- package/lib/shared/cmsQuery.test.ts +81 -0
- package/lib/shared/cmsQuery.ts +92 -0
- package/lib/shared/cmsQueryParser.ts +11 -7
- package/lib/shared/colorConversions.ts +9 -9
- package/lib/shared/colorVariableUtils.test.ts +22 -0
- package/lib/shared/colorVariableUtils.ts +19 -5
- package/lib/shared/constants.ts +4 -0
- package/lib/shared/cssGeneration.test.ts +23 -0
- package/lib/shared/cssGeneration.ts +40 -27
- package/lib/shared/cssProperties.test.ts +64 -64
- package/lib/shared/cssProperties.ts +2 -2
- package/lib/shared/elementClassName.ts +2 -2
- package/lib/shared/errorLogger.test.ts +12 -12
- package/lib/shared/expressionEvaluator.test.ts +27 -1
- package/lib/shared/expressionEvaluator.ts +9 -0
- package/lib/shared/fontLoader.test.ts +13 -13
- package/lib/shared/fontLoader.ts +2 -2
- package/lib/shared/gradientUtils.test.ts +2 -2
- package/lib/shared/gradientUtils.ts +15 -8
- package/lib/shared/hrefRefs.test.ts +2 -2
- package/lib/shared/hrefRefs.ts +1 -1
- package/lib/shared/i18n.test.ts +3 -3
- package/lib/shared/i18n.ts +5 -4
- package/lib/shared/index.ts +23 -0
- package/lib/shared/inlineSvgStyleRules.ts +12 -9
- package/lib/shared/interactiveStyleMappings.test.ts +21 -21
- package/lib/shared/interactiveStyleMappings.ts +2 -8
- package/lib/shared/itemTemplateUtils.test.ts +5 -5
- package/lib/shared/itemTemplateUtils.ts +9 -5
- package/lib/shared/jsonRepair.ts +8 -8
- package/lib/shared/libraryLoader.test.ts +6 -6
- package/lib/shared/linkUtils.ts +1 -1
- package/lib/shared/logger.test.ts +66 -0
- package/lib/shared/logger.ts +94 -0
- package/lib/shared/markdown.ts +25 -0
- package/lib/shared/nodeUtils.test.ts +13 -9
- package/lib/shared/nodeUtils.ts +27 -4
- package/lib/shared/pathSecurity.ts +1 -1
- package/lib/shared/pathUtils.ts +1 -2
- package/lib/shared/paths/PathConverter.ts +3 -3
- package/lib/shared/paths/PathUtils.ts +4 -3
- package/lib/shared/paths/PathValidator.ts +1 -1
- package/lib/shared/permissions.test.ts +168 -0
- package/lib/shared/permissions.ts +162 -0
- package/lib/shared/propResolver.test.ts +1 -1
- package/lib/shared/propResolver.ts +1 -1
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +5 -3
- package/lib/shared/registry/ClientRegistry.ts +0 -1
- package/lib/shared/registry/ComponentRegistry.test.ts +3 -3
- package/lib/shared/registry/ComponentRegistry.ts +6 -1
- package/lib/shared/registry/NodeTypeDefinition.ts +2 -2
- package/lib/shared/registry/SSRRegistry.ts +0 -1
- package/lib/shared/registry/fieldPresets.ts +3 -1
- package/lib/shared/registry/nodeTypes/CustomNodeType.ts +103 -0
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +1 -1
- package/lib/shared/registry/nodeTypes/IslandNodeType.ts +129 -0
- package/lib/shared/registry/nodeTypes/ListNodeType.ts +43 -7
- package/lib/shared/registry/nodeTypes/MarkdownNodeType.ts +78 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -1
- package/lib/shared/registry/nodeTypes/index.ts +16 -1
- package/lib/shared/responsiveScaling.test.ts +1 -3
- package/lib/shared/responsiveScaling.ts +6 -5
- package/lib/shared/responsiveStyleUtils.test.ts +0 -1
- package/lib/shared/richtext/htmlToTiptap.test.ts +116 -116
- package/lib/shared/richtext/htmlToTiptap.ts +1 -1
- package/lib/shared/styleNodeUtils.ts +17 -14
- package/lib/shared/themeDefaults.test.ts +0 -3
- package/lib/shared/tree/PathBuilder.test.ts +1 -7
- package/lib/shared/tree/PathBuilder.ts +15 -13
- package/lib/shared/treePathUtils.test.ts +49 -2
- package/lib/shared/treePathUtils.ts +40 -4
- package/lib/shared/types/api.ts +105 -7
- package/lib/shared/types/cms.test.ts +34 -0
- package/lib/shared/types/cms.ts +65 -13
- package/lib/shared/types/components.ts +24 -0
- package/lib/shared/types/errors.test.ts +1 -1
- package/lib/shared/types/index.ts +23 -0
- package/lib/shared/types/permissions.ts +139 -0
- package/lib/shared/types/variables.ts +3 -3
- package/lib/shared/utilityClassConfig.ts +103 -0
- package/lib/shared/utilityClassMapper.durability.test.ts +117 -0
- package/lib/shared/utilityClassMapper.test.ts +253 -26
- package/lib/shared/utilityClassMapper.ts +160 -29
- package/lib/shared/utilityClassNames.test.ts +38 -0
- package/lib/shared/utilityClassNames.ts +155 -23
- package/lib/shared/validation/index.ts +1 -0
- package/lib/shared/validation/permissionsValidators.test.ts +49 -0
- package/lib/shared/validation/permissionsValidators.ts +49 -0
- package/lib/shared/validation/propValidator.test.ts +1 -1
- package/lib/shared/validation/propValidator.ts +1 -1
- package/lib/shared/validation/schemas.test.ts +41 -0
- package/lib/shared/validation/schemas.ts +159 -13
- package/lib/shared/validation/validators.test.ts +2 -2
- package/lib/shared/viewportUnits.test.ts +13 -0
- package/lib/shared/viewportUnits.ts +12 -5
- package/lib/test-utils/factories/ConsoleMockFactory.ts +7 -7
- package/lib/test-utils/factories/DomMockFactory.ts +10 -4
- package/lib/test-utils/factories/ServerMockFactory.ts +1 -1
- package/lib/test-utils/factories/StoreMockFactory.ts +5 -5
- package/lib/test-utils/mockFactories.ts +1 -1
- package/lib/test-utils/mocks.ts +3 -3
- package/package.json +3 -5
- package/scripts/build-for-publish.mjs +2 -4
- package/scripts/build-meno-filter.ts +3 -3
- package/tsconfig.json +1 -0
- package/vite.config.ts +1 -1
- package/build-next.ts +0 -1374
- package/build-static.test.ts +0 -453
- package/build-static.ts +0 -1072
- package/dist/build-static.js +0 -37
- package/dist/chunks/chunk-2FN4UOVO.js +0 -6465
- package/dist/chunks/chunk-2FN4UOVO.js.map +0 -7
- package/dist/chunks/chunk-5ETZFREW.js.map +0 -7
- package/dist/chunks/chunk-7E4IF5L7.js +0 -245
- package/dist/chunks/chunk-7E4IF5L7.js.map +0 -7
- package/dist/chunks/chunk-7HWQUVTU.js.map +0 -7
- package/dist/chunks/chunk-AE3QK5QW.js.map +0 -7
- package/dist/chunks/chunk-F6KTJYGV.js +0 -322
- package/dist/chunks/chunk-F6KTJYGV.js.map +0 -7
- package/dist/chunks/chunk-FZITJSSS.js.map +0 -7
- package/dist/chunks/chunk-GSYYA5GX.js +0 -77
- package/dist/chunks/chunk-GSYYA5GX.js.map +0 -7
- package/dist/chunks/chunk-HIZMY3EP.js.map +0 -7
- package/dist/chunks/chunk-I2WEGYA7.js +0 -424
- package/dist/chunks/chunk-I2WEGYA7.js.map +0 -7
- package/dist/chunks/chunk-JNO3CNLJ.js +0 -464
- package/dist/chunks/chunk-JNO3CNLJ.js.map +0 -7
- package/dist/chunks/chunk-NVRBTSQG.js.map +0 -7
- package/dist/chunks/chunk-Q4OBWKXG.js +0 -1143
- package/dist/chunks/chunk-Q4OBWKXG.js.map +0 -7
- package/dist/chunks/chunk-QTE32Y53.js +0 -3829
- package/dist/chunks/chunk-QTE32Y53.js.map +0 -7
- package/dist/chunks/configService-PRJZF7Y6.js +0 -14
- package/dist/chunks/configService-PRJZF7Y6.js.map +0 -7
- package/dist/chunks/constants-KIQEYMAM.js +0 -46
- package/dist/chunks/constants-KIQEYMAM.js.map +0 -7
- package/dist/chunks/fs-ZI5JEU7V.js.map +0 -7
- package/dist/entries/server-router.js +0 -62
- package/dist/entries/server-router.js.map +0 -7
- package/entries/server-router.tsx +0 -75
- package/lib/server/providers/fileSystemPageProvider.test.ts +0 -84
- package/lib/server/providers/fileSystemPageProvider.ts +0 -179
- package/lib/server/webflow/buildWebflow.ts +0 -623
- package/lib/server/webflow/index.ts +0 -23
- package/lib/server/webflow/nodeToWebflow.test.ts +0 -3102
- package/lib/server/webflow/nodeToWebflow.ts +0 -2372
- package/lib/server/webflow/styleMapper.test.ts +0 -348
- package/lib/server/webflow/styleMapper.ts +0 -687
- package/lib/server/webflow/templateWrapper.ts +0 -49
- package/lib/server/webflow/types.ts +0 -389
- /package/dist/{build-static.js.map → chunks/fs-G3ANWEFP.js.map} +0 -0
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import type { ReactElement } from 'react';
|
|
7
7
|
import type { ZodType } from 'zod';
|
|
8
8
|
import type { ComponentNode } from '../types/nodes';
|
|
9
|
-
import type { StyleValue } from '../types/styles';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Tree icon types for node display
|
|
@@ -21,7 +20,8 @@ export type TreeIcon =
|
|
|
21
20
|
| 'ARRAY'
|
|
22
21
|
| 'UNKNOWN'
|
|
23
22
|
| 'FORM'
|
|
24
|
-
| 'LINK'
|
|
23
|
+
| 'LINK'
|
|
24
|
+
| 'CODE';
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Node category for grouping in command palette
|
|
@@ -33,7 +33,9 @@ interface TextFieldConfig extends BaseFieldConfig {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
interface SelectFieldConfig extends BaseFieldConfig {
|
|
36
|
-
options
|
|
36
|
+
// Bare strings are value-is-label options; the object form carries a distinct
|
|
37
|
+
// display label (e.g. an empty-value `{ value: '', label: 'None' }` entry).
|
|
38
|
+
options: (string | { value: string | boolean; label: string })[];
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
// ============================================
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Node Type Definition
|
|
3
|
+
*
|
|
4
|
+
* Represents a hand-authored, opaque `.astro` component (under `src/custom/`) that Meno
|
|
5
|
+
* treats as a BLACK BOX: it renders server-side in a real `astro build` / play preview
|
|
6
|
+
* (the meno-astro codec emits `<Fancy … />` + the import), but Meno doesn't model its
|
|
7
|
+
* internals — you can place it, pass props, and slot children, but not edit what's inside.
|
|
8
|
+
* The native-Astro sibling of an island (no framework renderer, no hydration).
|
|
9
|
+
*
|
|
10
|
+
* meno-core can't execute a foreign `.astro` file, so the editor canvas renders a quiet
|
|
11
|
+
* placeholder (its slotted children, or an inline marker) — real rendering is play/build
|
|
12
|
+
* only. The on-disk `.astro` form and the round-trip codec live in `meno-astro`; this file
|
|
13
|
+
* only models the node so it validates, persists, and renders a placeholder.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { createElement as h } from 'react';
|
|
18
|
+
import type { ComponentNode } from '../../types/nodes';
|
|
19
|
+
import { IfConditionSchema } from '../../validation/schemas';
|
|
20
|
+
import { createNodeType } from '../createNodeType';
|
|
21
|
+
import { textField } from '../fieldPresets';
|
|
22
|
+
|
|
23
|
+
// Schema is the SINGLE source of truth
|
|
24
|
+
const CustomNodeSchemaInternal = z
|
|
25
|
+
.object({
|
|
26
|
+
type: z.literal('custom'),
|
|
27
|
+
// Path to the hand-authored .astro component relative to `src/custom/`
|
|
28
|
+
// (e.g. "Fancy.astro", "widgets/Banner.astro").
|
|
29
|
+
src: z.string(),
|
|
30
|
+
props: z.record(z.string(), z.any()).optional(),
|
|
31
|
+
if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
|
|
32
|
+
// Slotted children (rendered server-side into the component's default <slot/>).
|
|
33
|
+
children: z.union([z.array(z.any()), z.string()]).optional(),
|
|
34
|
+
})
|
|
35
|
+
.passthrough();
|
|
36
|
+
|
|
37
|
+
// TypeScript type inferred from schema
|
|
38
|
+
export type CustomNode = z.infer<typeof CustomNodeSchemaInternal>;
|
|
39
|
+
|
|
40
|
+
// Export schema for validation/schemas.ts
|
|
41
|
+
export const CustomNodeSchema = CustomNodeSchemaInternal;
|
|
42
|
+
|
|
43
|
+
/** Basename of the custom source (drops directory + `.astro` extension) for display. */
|
|
44
|
+
function customName(src: string): string {
|
|
45
|
+
const base = src.split('/').pop() ?? src;
|
|
46
|
+
return base.replace(/\.astro$/i, '') || base;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Slotted children, normalized to the shape `buildChildren`/`renderChildren` accept. */
|
|
50
|
+
function customChildren(node: CustomNode): Array<ComponentNode | string> | string | undefined {
|
|
51
|
+
const { children } = node;
|
|
52
|
+
if (Array.isArray(children)) return children.length > 0 ? (children as Array<ComponentNode | string>) : undefined;
|
|
53
|
+
if (typeof children === 'string') return children.length > 0 ? children : undefined;
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const CustomNodeType = createNodeType({
|
|
58
|
+
type: 'custom',
|
|
59
|
+
displayName: 'Custom Component',
|
|
60
|
+
category: 'special',
|
|
61
|
+
schema: CustomNodeSchemaInternal,
|
|
62
|
+
|
|
63
|
+
defaultValues: {
|
|
64
|
+
src: '',
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
treeDisplay: {
|
|
68
|
+
icon: 'COMPONENT',
|
|
69
|
+
getLabel: (node) => `Custom: ${customName(node.src) || '?'}`,
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
editableFields: [textField({ name: 'src', label: 'Custom .astro file', required: true, placeholder: 'Fancy.astro' })],
|
|
73
|
+
|
|
74
|
+
// meno-core can't run a foreign `.astro` file. Render the slotted children as normal
|
|
75
|
+
// content and fall back to a quiet inline marker when there are none. The wrapper keeps
|
|
76
|
+
// `data-custom` for selection/identification only. Real rendering is play/build only.
|
|
77
|
+
clientRenderer: (node, context) => {
|
|
78
|
+
const children = customChildren(node);
|
|
79
|
+
const inner = children
|
|
80
|
+
? context.buildChildren(children, context.elementPath)
|
|
81
|
+
: h('span', { style: { color: '#9ca3af', fontSize: '13px' } }, customName(node.src) || '?');
|
|
82
|
+
return h('div', { key: context.key, 'data-custom': node.src, style: { display: 'contents' } }, inner);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
ssrRenderer: (node, context) => {
|
|
86
|
+
const children = customChildren(node);
|
|
87
|
+
const inner = children
|
|
88
|
+
? context.renderChildren(children, context)
|
|
89
|
+
: `<span style="color:#9ca3af;font-size:13px">${escapeHtml(customName(node.src) || '?')}</span>`;
|
|
90
|
+
return `<div data-custom="${escapeHtml(node.src)}" style="display:contents">${inner}</div>`;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
capabilities: {
|
|
94
|
+
canHaveChildren: true,
|
|
95
|
+
canHaveStyle: false,
|
|
96
|
+
canHaveAttributes: false,
|
|
97
|
+
requiresProps: ['src'],
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
function escapeHtml(value: string): string {
|
|
102
|
+
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
103
|
+
}
|
|
@@ -51,7 +51,7 @@ export const HtmlNodeType = createNodeType({
|
|
|
51
51
|
},
|
|
52
52
|
|
|
53
53
|
clientRenderer: (node, context) => {
|
|
54
|
-
const props: Record<string,
|
|
54
|
+
const props: Record<string, unknown> = { key: context.key };
|
|
55
55
|
|
|
56
56
|
// Prevent form submission in editor
|
|
57
57
|
if (node.tag === 'form') {
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Island Node Type Definition
|
|
3
|
+
*
|
|
4
|
+
* Represents a BYO framework component (React/Preact/Vue/Svelte) rendered as an
|
|
5
|
+
* Astro island. The actual hydration only happens in a real `astro build` / play
|
|
6
|
+
* preview (the meno-astro codec emits `<Counter client:visible … />`). meno-core
|
|
7
|
+
* itself can't run a framework component, so the editor canvas renders a visible
|
|
8
|
+
* placeholder — design-time positioning works, interactivity is play/build only.
|
|
9
|
+
*
|
|
10
|
+
* The on-disk `.astro` form and the round-trip codec live in `meno-astro`; this
|
|
11
|
+
* file only models the node so it validates, persists, and renders a placeholder.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { createElement as h } from 'react';
|
|
16
|
+
import type { ComponentNode } from '../../types/nodes';
|
|
17
|
+
import { IfConditionSchema } from '../../validation/schemas';
|
|
18
|
+
import { createNodeType } from '../createNodeType';
|
|
19
|
+
import { textField, selectField } from '../fieldPresets';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Client (hydration) directive for an island.
|
|
23
|
+
* `directive` maps to Astro's `client:*` directive; `value` carries the media
|
|
24
|
+
* query (`client:media="(max-width: 50em)"`) or the framework name
|
|
25
|
+
* (`client:only="react"`). When omitted entirely, the island is server-rendered
|
|
26
|
+
* with no JS (a valid, zero-hydration Astro island).
|
|
27
|
+
*/
|
|
28
|
+
export const IslandClientDirectiveSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
directive: z.enum(['load', 'idle', 'visible', 'media', 'only']),
|
|
31
|
+
value: z.string().optional(),
|
|
32
|
+
})
|
|
33
|
+
.passthrough();
|
|
34
|
+
|
|
35
|
+
// Schema is the SINGLE source of truth
|
|
36
|
+
const IslandNodeSchemaInternal = z
|
|
37
|
+
.object({
|
|
38
|
+
type: z.literal('island'),
|
|
39
|
+
// Path to the framework component file relative to `src/islands/`
|
|
40
|
+
// (e.g. "Counter.tsx", "widgets/Chart.vue"). The extension derives the
|
|
41
|
+
// framework, which drives the Astro integration that gets provisioned.
|
|
42
|
+
src: z.string(),
|
|
43
|
+
client: IslandClientDirectiveSchema.optional(),
|
|
44
|
+
props: z.record(z.string(), z.any()).optional(),
|
|
45
|
+
if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
|
|
46
|
+
// Slotted children (rendered server-side into the island's <slot/>).
|
|
47
|
+
children: z.union([z.array(z.any()), z.string()]).optional(),
|
|
48
|
+
})
|
|
49
|
+
.passthrough();
|
|
50
|
+
|
|
51
|
+
// TypeScript type inferred from schema
|
|
52
|
+
export type IslandNode = z.infer<typeof IslandNodeSchemaInternal>;
|
|
53
|
+
|
|
54
|
+
// Export schema for validation/schemas.ts
|
|
55
|
+
export const IslandNodeSchema = IslandNodeSchemaInternal;
|
|
56
|
+
|
|
57
|
+
/** Basename of the island source (drops directory + extension) for display. */
|
|
58
|
+
function islandName(src: string): string {
|
|
59
|
+
const base = src.split('/').pop() ?? src;
|
|
60
|
+
return base.replace(/\.(tsx|jsx|vue|svelte)$/i, '') || base;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Slotted children, normalized to the shape `buildChildren`/`renderChildren` accept. */
|
|
64
|
+
function islandChildren(node: IslandNode): Array<ComponentNode | string> | string | undefined {
|
|
65
|
+
const { children } = node;
|
|
66
|
+
if (Array.isArray(children)) return children.length > 0 ? (children as Array<ComponentNode | string>) : undefined;
|
|
67
|
+
if (typeof children === 'string') return children.length > 0 ? children : undefined;
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const IslandNodeType = createNodeType({
|
|
72
|
+
type: 'island',
|
|
73
|
+
displayName: 'Island',
|
|
74
|
+
category: 'special',
|
|
75
|
+
schema: IslandNodeSchemaInternal,
|
|
76
|
+
|
|
77
|
+
defaultValues: {
|
|
78
|
+
src: '',
|
|
79
|
+
client: { directive: 'load' },
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
treeDisplay: {
|
|
83
|
+
icon: 'COMPONENT',
|
|
84
|
+
getLabel: (node) => `Island: ${islandName(node.src) || '?'}`,
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
editableFields: [
|
|
88
|
+
textField({ name: 'src', label: 'Island file', required: true, placeholder: 'Counter.tsx' }),
|
|
89
|
+
selectField({
|
|
90
|
+
name: 'client.directive',
|
|
91
|
+
label: 'Hydration',
|
|
92
|
+
// The empty-value entry clears `client` entirely → a server-rendered island
|
|
93
|
+
// with zero JS (a valid Astro pattern); the rest map to `client:<directive>`.
|
|
94
|
+
options: [{ value: '', label: 'None (server-only, no JS)' }, 'load', 'idle', 'visible', 'media', 'only'],
|
|
95
|
+
}),
|
|
96
|
+
],
|
|
97
|
+
|
|
98
|
+
// meno-core can't run a framework component. Instead of a loud highlight box
|
|
99
|
+
// (which made island-heavy pages unreadable), render the island's slotted
|
|
100
|
+
// children as normal content and fall back to a quiet inline marker when there
|
|
101
|
+
// are none. The wrapper keeps `data-island` for selection/identification only.
|
|
102
|
+
// Real hydration is play/build only.
|
|
103
|
+
clientRenderer: (node, context) => {
|
|
104
|
+
const children = islandChildren(node);
|
|
105
|
+
const inner = children
|
|
106
|
+
? context.buildChildren(children, context.elementPath)
|
|
107
|
+
: h('span', { style: { color: '#9ca3af', fontSize: '13px' } }, islandName(node.src) || '?');
|
|
108
|
+
return h('div', { key: context.key, 'data-island': node.src, style: { display: 'contents' } }, inner);
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
ssrRenderer: (node, context) => {
|
|
112
|
+
const children = islandChildren(node);
|
|
113
|
+
const inner = children
|
|
114
|
+
? context.renderChildren(children, context)
|
|
115
|
+
: `<span style="color:#9ca3af;font-size:13px">${escapeHtml(islandName(node.src) || '?')}</span>`;
|
|
116
|
+
return `<div data-island="${escapeHtml(node.src)}" style="display:contents">${inner}</div>`;
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
capabilities: {
|
|
120
|
+
canHaveChildren: true,
|
|
121
|
+
canHaveStyle: false,
|
|
122
|
+
canHaveAttributes: false,
|
|
123
|
+
requiresProps: ['src'],
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
function escapeHtml(value: string): string {
|
|
128
|
+
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
129
|
+
}
|
|
@@ -41,14 +41,38 @@ const ListNodeSchemaInternal = z
|
|
|
41
41
|
* Data source type:
|
|
42
42
|
* - 'prop': Read items from component props (default)
|
|
43
43
|
* - 'collection': Query items from CMS collection
|
|
44
|
+
* - 'remote': Fetch items from a public HTTP/JSON endpoint at build/SSR time
|
|
45
|
+
* - 'sanity': Fetch a Sanity document type via GROQ at build/SSR time (read-only, public dataset)
|
|
46
|
+
* - 'expression': Iterate a verbatim JS expression (`source`) that yields an array — a
|
|
47
|
+
* `.map()` over arbitrary frontmatter data (e.g. an Astro Action / supabase query) the
|
|
48
|
+
* dialect can't model as a prop/collection. The item template (`children` + `itemAs`) IS
|
|
49
|
+
* editable; the source itself is a read-only black box (edit it in the .astro frontmatter).
|
|
50
|
+
* The editor has no data for it, so it renders empty in the design canvas (real Astro play
|
|
51
|
+
* renders it for real at build/SSR).
|
|
44
52
|
*/
|
|
45
|
-
sourceType: z.enum(['prop', 'collection']).default('prop'),
|
|
53
|
+
sourceType: z.enum(['prop', 'collection', 'remote', 'sanity', 'expression']).default('prop'),
|
|
46
54
|
/**
|
|
47
|
-
* Source identifier:
|
|
55
|
+
* Source identifier (sourceType 'prop'/'collection'/'expression'; 'remote' uses `url`):
|
|
48
56
|
* - For sourceType 'prop': Prop name (e.g., "items") or template expression (e.g., "{{category.items}}")
|
|
49
57
|
* - For sourceType 'collection': Collection name (e.g., "posts", "authors")
|
|
58
|
+
* - For sourceType 'expression': the verbatim JS expression iterated by `.map()` (e.g. "notes?")
|
|
50
59
|
*/
|
|
51
|
-
source: z.string(),
|
|
60
|
+
source: z.string().optional(),
|
|
61
|
+
|
|
62
|
+
// Remote-only options (sourceType: 'remote')
|
|
63
|
+
/** HTTP(S) endpoint returning JSON, fetched at build/SSR time (e.g. a public REST API). */
|
|
64
|
+
url: z.string().optional(),
|
|
65
|
+
/**
|
|
66
|
+
* Dot-path into the JSON response to reach the items array (e.g. "data.items").
|
|
67
|
+
* Empty/absent = the response body is itself the array.
|
|
68
|
+
*/
|
|
69
|
+
path: z.string().optional(),
|
|
70
|
+
|
|
71
|
+
// Sanity-only options (sourceType: 'sanity')
|
|
72
|
+
/** Sanity document `_type` to fetch (`*[_type == "<documentType>"]`). projectId/dataset come from project config. */
|
|
73
|
+
documentType: z.string().optional(),
|
|
74
|
+
/** Field used as the URL slug (for synthesizing item links); defaults to 'slug'. */
|
|
75
|
+
slugField: z.string().optional(),
|
|
52
76
|
label: z.string().optional(), // Custom label displayed in structure tree
|
|
53
77
|
if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
|
|
54
78
|
/**
|
|
@@ -111,8 +135,19 @@ export const ListNodeType = createNodeType({
|
|
|
111
135
|
getLabel: (node) => {
|
|
112
136
|
const listNode = node as ListNode;
|
|
113
137
|
const sourceType = listNode.sourceType || 'prop';
|
|
114
|
-
const prefix =
|
|
115
|
-
|
|
138
|
+
const prefix =
|
|
139
|
+
sourceType === 'collection'
|
|
140
|
+
? 'CMS List'
|
|
141
|
+
: sourceType === 'remote'
|
|
142
|
+
? 'Remote List'
|
|
143
|
+
: sourceType === 'sanity'
|
|
144
|
+
? 'Sanity List'
|
|
145
|
+
: sourceType === 'expression'
|
|
146
|
+
? 'Loop'
|
|
147
|
+
: 'List';
|
|
148
|
+
const src =
|
|
149
|
+
sourceType === 'remote' ? listNode.url : sourceType === 'sanity' ? listNode.documentType : listNode.source;
|
|
150
|
+
return src ? `${prefix}: ${src}` : prefix;
|
|
116
151
|
},
|
|
117
152
|
},
|
|
118
153
|
|
|
@@ -132,7 +167,8 @@ export const ListNodeType = createNodeType({
|
|
|
132
167
|
key: context.key,
|
|
133
168
|
'data-list': 'true',
|
|
134
169
|
'data-source-type': sourceType,
|
|
135
|
-
'data-source':
|
|
170
|
+
'data-source':
|
|
171
|
+
sourceType === 'remote' ? listNode.url : sourceType === 'sanity' ? listNode.documentType : listNode.source,
|
|
136
172
|
style: {
|
|
137
173
|
padding: '8px 12px',
|
|
138
174
|
background: bgColor,
|
|
@@ -162,7 +198,7 @@ export const ListNodeType = createNodeType({
|
|
|
162
198
|
label: 'Source Type',
|
|
163
199
|
type: 'select',
|
|
164
200
|
required: false,
|
|
165
|
-
options: ['prop', 'collection'],
|
|
201
|
+
options: ['prop', 'collection', 'remote', 'sanity'],
|
|
166
202
|
},
|
|
167
203
|
{
|
|
168
204
|
name: 'source',
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Node Type Definition
|
|
3
|
+
* Renders a verbatim Markdown source string as HTML (Astro `.md`-style authoring).
|
|
4
|
+
*
|
|
5
|
+
* The body is stored byte-exact in `node.source` (whitespace-significant Markdown). meno-core
|
|
6
|
+
* renders it via `renderMarkdown` for the canvas + SSR; the meno-astro codec hoists the source
|
|
7
|
+
* to a backtick const and renders it through the runtime `renderMarkdown` at build (see
|
|
8
|
+
* dialect/emit/emitNode.ts:renderMarkdown). Modeled on the Embed node (verbatim payload).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { createElement as h } from 'react';
|
|
13
|
+
import { renderMarkdown } from '../../markdown';
|
|
14
|
+
import { StyleValueSchema, InteractiveStylesSchema, IfConditionSchema } from '../../validation/schemas';
|
|
15
|
+
import { createNodeType } from '../createNodeType';
|
|
16
|
+
import { textareaField } from '../fieldPresets';
|
|
17
|
+
|
|
18
|
+
// Schema is the SINGLE source of truth
|
|
19
|
+
const MarkdownNodeSchemaInternal = z
|
|
20
|
+
.object({
|
|
21
|
+
type: z.literal('markdown'),
|
|
22
|
+
source: z.string(), // verbatim Markdown source (whitespace-significant)
|
|
23
|
+
label: z.string().optional(), // Custom label displayed in structure tree
|
|
24
|
+
if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
|
|
25
|
+
style: StyleValueSchema.optional(),
|
|
26
|
+
interactiveStyles: InteractiveStylesSchema.optional(),
|
|
27
|
+
generateElementClass: z.boolean().optional(),
|
|
28
|
+
attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
|
|
29
|
+
})
|
|
30
|
+
.passthrough();
|
|
31
|
+
|
|
32
|
+
// TypeScript type inferred from schema
|
|
33
|
+
export type MarkdownNode = z.infer<typeof MarkdownNodeSchemaInternal>;
|
|
34
|
+
|
|
35
|
+
// Export schema for validation/schemas.ts
|
|
36
|
+
export const MarkdownNodeSchema = MarkdownNodeSchemaInternal;
|
|
37
|
+
|
|
38
|
+
export const MarkdownNodeType = createNodeType({
|
|
39
|
+
type: 'markdown',
|
|
40
|
+
displayName: 'Markdown',
|
|
41
|
+
category: 'content',
|
|
42
|
+
schema: MarkdownNodeSchemaInternal,
|
|
43
|
+
|
|
44
|
+
defaultValues: {
|
|
45
|
+
source: '',
|
|
46
|
+
style: { base: {} },
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
treeDisplay: {
|
|
50
|
+
icon: 'HTML_ELEMENT',
|
|
51
|
+
getLabel: () => 'Markdown',
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
editableFields: [
|
|
55
|
+
textareaField({
|
|
56
|
+
name: 'source',
|
|
57
|
+
label: 'Markdown',
|
|
58
|
+
required: true,
|
|
59
|
+
placeholder: '# Heading\n\nWrite **markdown** here…',
|
|
60
|
+
}),
|
|
61
|
+
],
|
|
62
|
+
|
|
63
|
+
clientRenderer: (node, context) => {
|
|
64
|
+
return h('div', {
|
|
65
|
+
key: context.key,
|
|
66
|
+
dangerouslySetInnerHTML: { __html: renderMarkdown((node as { source?: unknown }).source) },
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
ssrRenderer: (node, _context) => {
|
|
71
|
+
return `<div>${renderMarkdown((node as { source?: unknown }).source)}</div>`;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
capabilities: {
|
|
75
|
+
canHaveChildren: false,
|
|
76
|
+
requiresProps: ['source'],
|
|
77
|
+
},
|
|
78
|
+
});
|
|
@@ -32,7 +32,12 @@ export const SlotMarkerType = createNodeType({
|
|
|
32
32
|
|
|
33
33
|
treeDisplay: {
|
|
34
34
|
icon: 'SLOT_MARKER',
|
|
35
|
-
|
|
35
|
+
// Show the slot's name so multiple named slots in a component are distinguishable
|
|
36
|
+
// (e.g. `slot: header`). Unnamed (default) slots stay just `slot`.
|
|
37
|
+
getLabel: (node) => {
|
|
38
|
+
const name = (node as { name?: unknown }).name;
|
|
39
|
+
return typeof name === 'string' && name ? `slot: ${name}` : 'slot';
|
|
40
|
+
},
|
|
36
41
|
},
|
|
37
42
|
|
|
38
43
|
clientRenderer: (_node, _context) => {
|
|
@@ -21,6 +21,9 @@ import { EmbedNodeType, EmbedNodeSchema, type EmbedNode } from './EmbedNodeType'
|
|
|
21
21
|
import { LinkNodeType, LinkNodeSchema, type LinkNode } from './LinkNodeType';
|
|
22
22
|
import { LocaleListNodeType, LocaleListNodeSchema, type LocaleListNode } from './LocaleListNodeType';
|
|
23
23
|
import { ListNodeType, ListNodeSchema, type ListNode } from './ListNodeType';
|
|
24
|
+
import { IslandNodeType, IslandNodeSchema, type IslandNode } from './IslandNodeType';
|
|
25
|
+
import { MarkdownNodeType, MarkdownNodeSchema, type MarkdownNode } from './MarkdownNodeType';
|
|
26
|
+
import { CustomNodeType, CustomNodeSchema, type CustomNode } from './CustomNodeType';
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
29
|
* All built-in node type definitions
|
|
@@ -34,6 +37,9 @@ export const builtInNodeTypes: NodeTypeDefinition[] = [
|
|
|
34
37
|
LinkNodeType as NodeTypeDefinition,
|
|
35
38
|
LocaleListNodeType as NodeTypeDefinition,
|
|
36
39
|
ListNodeType as NodeTypeDefinition,
|
|
40
|
+
IslandNodeType as NodeTypeDefinition,
|
|
41
|
+
MarkdownNodeType as NodeTypeDefinition,
|
|
42
|
+
CustomNodeType as NodeTypeDefinition,
|
|
37
43
|
];
|
|
38
44
|
|
|
39
45
|
/**
|
|
@@ -47,6 +53,9 @@ export const nodeSchemas = {
|
|
|
47
53
|
link: LinkNodeSchema,
|
|
48
54
|
localeList: LocaleListNodeSchema,
|
|
49
55
|
list: ListNodeSchema,
|
|
56
|
+
island: IslandNodeSchema,
|
|
57
|
+
markdown: MarkdownNodeSchema,
|
|
58
|
+
custom: CustomNodeSchema,
|
|
50
59
|
};
|
|
51
60
|
|
|
52
61
|
/**
|
|
@@ -69,6 +78,9 @@ export { EmbedNodeType, EmbedNodeSchema, type EmbedNode } from './EmbedNodeType'
|
|
|
69
78
|
export { LinkNodeType, LinkNodeSchema, type LinkNode } from './LinkNodeType';
|
|
70
79
|
export { LocaleListNodeType, LocaleListNodeSchema, type LocaleListNode } from './LocaleListNodeType';
|
|
71
80
|
export { ListNodeType, ListNodeSchema, type ListNode } from './ListNodeType';
|
|
81
|
+
export { IslandNodeType, IslandNodeSchema, type IslandNode } from './IslandNodeType';
|
|
82
|
+
export { MarkdownNodeType, MarkdownNodeSchema, type MarkdownNode } from './MarkdownNodeType';
|
|
83
|
+
export { CustomNodeType, CustomNodeSchema, type CustomNode } from './CustomNodeType';
|
|
72
84
|
|
|
73
85
|
/**
|
|
74
86
|
* Discriminated union of all component node types
|
|
@@ -81,4 +93,7 @@ export type ComponentNode =
|
|
|
81
93
|
| EmbedNode
|
|
82
94
|
| LinkNode
|
|
83
95
|
| LocaleListNode
|
|
84
|
-
| ListNode
|
|
96
|
+
| ListNode
|
|
97
|
+
| IslandNode
|
|
98
|
+
| MarkdownNode
|
|
99
|
+
| CustomNode;
|
|
@@ -15,8 +15,6 @@ import {
|
|
|
15
15
|
buildSiteMarginClamp,
|
|
16
16
|
getSmallestBreakpointName,
|
|
17
17
|
type ResponsiveScales,
|
|
18
|
-
type CSSPropertyType,
|
|
19
|
-
type BreakpointScales,
|
|
20
18
|
} from './responsiveScaling';
|
|
21
19
|
|
|
22
20
|
describe('responsiveScaling', () => {
|
|
@@ -454,7 +452,7 @@ describe('responsiveScaling', () => {
|
|
|
454
452
|
test('multi-value padding "20px 40px" → both tokens scaled', () => {
|
|
455
453
|
const out = buildFluidPropertyValue('20px 40px', 0.5, 320, 1440);
|
|
456
454
|
expect(out).not.toBeNull();
|
|
457
|
-
const
|
|
455
|
+
const _parts = out!.split(' ');
|
|
458
456
|
// The two tokens get joined by space — but each clamp() also has spaces inside.
|
|
459
457
|
// Check both clamp(...) substrings present.
|
|
460
458
|
expect((out!.match(/clamp\(/g) ?? []).length).toBe(2);
|
|
@@ -111,8 +111,8 @@ export function parseValue(valueStr: string): { value: number; unit: string } |
|
|
|
111
111
|
const match = valueStr.trim().match(/^(-?[\d.]+)(px|rem|em|%|pt)$/);
|
|
112
112
|
if (!match) return null;
|
|
113
113
|
return {
|
|
114
|
-
value: parseFloat(match[1]),
|
|
115
|
-
unit: match[2],
|
|
114
|
+
value: parseFloat(match[1] ?? ''),
|
|
115
|
+
unit: match[2] ?? '',
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -339,9 +339,10 @@ export function getSmallestBreakpointName(
|
|
|
339
339
|
): string | null {
|
|
340
340
|
if (!breakpoints) return null;
|
|
341
341
|
const entries = Object.entries(breakpoints);
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
let
|
|
342
|
+
const firstEntry = entries[0];
|
|
343
|
+
if (entries.length === 0 || firstEntry === undefined) return null;
|
|
344
|
+
let smallestName = firstEntry[0];
|
|
345
|
+
let smallestWidth = firstEntry[1].breakpoint;
|
|
345
346
|
for (const [name, cfg] of entries) {
|
|
346
347
|
if (cfg.breakpoint < smallestWidth) {
|
|
347
348
|
smallestWidth = cfg.breakpoint;
|